Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions panel/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -108,13 +108,13 @@
<button id="add-testCase" style="display: none">+Case</button>
<button id="delete-testCase" style="display: none">-Case</button>
<a download="info.html" id="downloadlink" style="display: none">Download</a>

<a name="" target="_blank" id="logo_kr_icon" class="sub_btn"><img src="../katalon/images/branding/branding_128.png" alt="Katalon Recorder" /></button>
<a name="" target="_blank" id="logo_kr_full" class="sub_btn">
<img class="dark" src="../katalon/images/branding/Katalon-Recorder-full-color-large.png" alt="Katalon Recorder" />
<img class="light" src="../katalon/images/branding/Katalon-Recorder-full-color-large-w.png" alt="Katalon Recorder" />
</a>

<button id="new" class="sub_btn"><i class="fa"></i> New</button>
<button id="record" class="sub_btn" style="width: 15%"><i class="fa fa-video-camera" aria-hidden="true"></i> Rec.</button>
<button id="playback" class="sub_btn" style="width: 20%" disabled><i class="fa fa-play" aria-hidden="true"></i> Play</button>
Expand Down Expand Up @@ -420,6 +420,7 @@ <h3 class="suite-container-title">
<option value="python-appdynamics">Python (AppDynamics)</option>
<option value="robot">Robot Framework</option>
<option value="ruby-wd-rspec">Ruby (WebDriver + RSpec)</option>
<option value="new-formatter-webdriver">WebDriver.io</option>
<option value="xml">XML</option>
<!-- must start with new-formatter- -->
<option value="new-formatter-sample">Sample for new formatters</option>
Expand Down Expand Up @@ -481,5 +482,6 @@ <h1>Need help? Explore Katalon Recorder Support Resources</h1>
<script type="text/javascript" src="js/katalon/newformatters/sample.js"></script>
<script type="text/javascript" src="js/katalon/newformatters/nrsynthetics.js"></script>
<script type="text/javascript" src="js/katalon/newformatters/protractorts.js"></script>
<script type="text/javascript" src="js/katalon/newformatters/webdriver.js"></script>
</body>
</html>
</html>
174 changes: 174 additions & 0 deletions panel/js/katalon/newformatters/webdriver.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
newFormatters.webdriver = function (name, commands) {
let content = newWebDriver(name).formatter(commands);
return {
content: content,
extension: 'js',
mimetype: 'text/javascript'
}
}

const newWebDriver = function (scriptName) {
let _scriptName = scriptName || "";
const locatorType = {
xpath: (target) => {
alert(target);
return `'${target.replace(/'/g, "\\\'")}'`;
},
css: (target) => {
return `'${target.replace(/"/g, "\'")}'`;
},
id: (target) => {
return `'#${target.replace(/"/g, "\'")}'`;
},
link: (target) => {
return `'=${target.replace(/"/g, "\'")}'`;
},
name: (target) => {
return `'[name="${target.replace(/"/g, "\'")}"]'`;
},
tag_name: (target) => {
return `'${target.replace(/"/g, "\'")}'`;
}
};

// https://w3c.github.io/webdriver/#keyboard-actions
const specialKeyMap = {
'\${KEY_LEFT}': 'ArrowLeft',
'\${KEY_UP}': 'ArrowUp',
'\${KEY_RIGHT}': 'ArrowRight',
'\${KEY_DOWN}': 'ArrowDown',
'\${KEY_PAGE_UP}': 'PageUp',
'\${KEY_PAGE_DOWN}': 'PageDown',
'\${KEY_BACKSPACE}': 'Backspace',
'\${KEY_DEL}': 'Delete',
'\${KEY_DELETE}': 'Delete',
'\${KEY_ENTER}': 'Key.ENTER',
'\${KEY_TAB}': 'Tab',
'\${KEY_HOME}': 'Home',
'\${KEY_END}': 'End'
};

// webdriver api
// https://webdriver.io/docs/api.html
// katalon
// https://docs.katalon.com/katalon-recorder/docs/selenese-selenium-ide-commands-reference.html
const seleneseCommands = {
"open": "browser.url('_TARGET_');",
"click": "$(_BY_LOCATOR_).click();",
"clickAndWait":
"const el__STEP_ = $(_BY_LOCATOR_);\n" +
"\t\tel__STEP_.waitForClickable();\n" +
"\t\tel__STEP_.click();",
"doubleClick": "$(_BY_LOCATOR_).doubleClick();",
"doubleClickAndWait":
"const el__STEP_ = $(_BY_LOCATOR_);\n" +
"\t\tel__STEP_.waitForClickable();\n" +
"\t\tel__STEP_.doubleClick();",
"type": "$(_BY_LOCATOR_).setValue('_VALUE_');",
"typeAndWait":
"const el__STEP_ = $(_BY_LOCATOR_);\n" +
"\t\tel__STEP_.waitForClickable();\n" +
"\t\tel__STEP_.setValue('_VALUE_');",
"pause": "browser.pause(_VALUE_);",
"refresh": "browser.refresh();",
"sendKeys": "await $(_BY_LOCATOR_).sendKeys(_SEND_KEY_);",
"sendKeysAndWait":
"const el__STEP_ = $(_BY_LOCATOR_);\n" +
"\t\tel__STEP_.waitForClickable();\n" +
"\t\tel__STEP_.setValue('_VALUE_');",
"select": "$(_BY_LOCATOR_).selectByVisibleText('_SELECT_OPTION_');",
"goBack": "browser.back();",
"assertConfirmation": "browser.acceptAlert()",
"verifyText": "expect( $(_BY_LOCATOR_)).toHaveTextContaining(`_VALUE_STR_`);",
"verifyTitle": "expect( browser).toHaveTitle(`_VALUE_STR_`);",
"verifyValue": "expect( $(_BY_LOCATOR_)).toHaveValueContaining(`_VALUE_STR_`)",
"assertText": "expect( $(_BY_LOCATOR_)).toHaveTextContaining(`_VALUE_STR_`);",
"assertTitle": "expect( browser).toHaveTitle(`_VALUE_STR_`);",
"assertValue": "expect( $(_BY_LOCATOR_)).toHaveValueContaining(`_VALUE_STR_`)",
"waitForAlertPresent":
"browser.waitUntil(function() {\n" +
"\t\t\treturn browser.getAlertText()\n" +
"\t\t})",
"waitForElementPresent": "$(_BY_LOCATOR_).waitForExist();",
"waitForValue":
"const el__STEP_ = $(_BY_LOCATOR_);\n" +
"\t\tbrowser.waitUntil(() => el__STEP_.getValue() === `_VALUE_STR_`);",
"waitForNotValue":
"const el__STEP_ = $(_BY_LOCATOR_);\n" +
"\t\tbrowser.waitUntil(() => el__STEP_.getValue() !== `_VALUE_STR_`);",
"waitForVisible": "$(_BY_LOCATOR_).waitForDisplayed();",
};

const header =
"var assert = require('assert');\n\n" +
"describe('_SCRIPT_NAME_', function() {\n\n" +
"\tit('should do something', function() {\n";

const footer = "\t});\n\n});";

function formatter(commands) {

return header.replace(/_SCRIPT_NAME_/g, _scriptName) +
commandExports(commands).content +
footer;
}

function commandExports(commands) {

return commands.reduce((accObj, commandObj) => {
let { command, target, value } = commandObj;
let cmd = seleneseCommands[command];
if ( typeof (cmd) == "undefined" ) {
accObj.content += `\n\n\t// WARNING: unsupported command ${command}. Object= ${JSON.stringify(commandObj)}\n\n`;
return accObj;
}

let funcStr = cmd;

if ( typeof (accObj) == "undefined" ) {
accObj = { content: "" };
}

let targetStr = target.trim().replace(/'/g, "\\'")
.replace(/"/g, '\\"');

let valueStr = value.trim().replace(/'/g, "\\'")
.replace(/"/g, '\\"');

let selectOption = value.trim().split("=", 2)[1];

let locatorStr = locator(target);

funcStr = funcStr.replace(/_STEP_/g, accObj.step)
.replace(/_TARGET_STR_/g, targetStr)
.replace(/_BY_LOCATOR_/g, locatorStr)
.replace(/_TARGET_/g, target)
.replace(/_SEND_KEY_/g, specialKeyMap[value])
.replace(/_VALUE_STR_/g, valueStr)
.replace(/_VALUE_/g, value)
.replace(/_SELECT_OPTION_/g, selectOption);

accObj.step += 1;
accObj.content += `\t\t${funcStr}\n`

return accObj;
}, { step: 1, content: "" });
}

function locator(target) {
let locType = target.split("=", 1);
let selectorStr = target.substr(target.indexOf("=") + 1, target.length);
let locatorFunc = locatorType[locType];
if ( typeof (locatorFunc) == 'undefined' ) {
return `'${target.replace(/'/g, '"')}'`;
}

return locatorFunc(selectorStr);
}

return {
formatter,
locator
};
}