Skip to content

Commit 3d2030d

Browse files
committed
Merge branch 'master' of github.com:SeleniumHQ/selenium-ide
2 parents d2ddd23 + f50fd9e commit 3d2030d

File tree

20 files changed

+532
-315
lines changed

20 files changed

+532
-315
lines changed

packages/selenium-ide/src/content/targetSelector.js

Lines changed: 76 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -25,95 +25,96 @@ export function exactMatchPattern(string) {
2525
}
2626
}
2727

28-
function TargetSelector(callback, cleanupCallback) {
29-
this.callback = callback;
30-
this.cleanupCallback = cleanupCallback;
28+
class TargetSelector {
29+
constructor(callback, cleanupCallback) {
30+
this.callback = callback;
31+
this.cleanupCallback = cleanupCallback;
32+
// This is for XPCOM/XUL addon and can't be used
33+
//var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService(Components.interfaces.nsIWindowMediator);
34+
//this.win = wm.getMostRecentWindow('navigator:browser').getBrowser().contentWindow;
3135

32-
// This is for XPCOM/XUL addon and can't be used
33-
//var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService(Components.interfaces.nsIWindowMediator);
34-
//this.win = wm.getMostRecentWindow('navigator:browser').getBrowser().contentWindow;
35-
36-
// Instead, we simply assign global content window to this.win
37-
this.win = window;
38-
const doc = this.win.document;
39-
const div = doc.createElement("div");
40-
div.setAttribute("style", "display: none;");
41-
doc.body.insertBefore(div, doc.body.firstChild);
42-
this.div = div;
43-
this.e = null;
44-
this.r = null;
45-
doc.addEventListener("mousemove", this, true);
46-
doc.addEventListener("click", this, true);
47-
}
36+
// Instead, we simply assign global content window to this.win
37+
this.win = window;
38+
const doc = this.win.document;
39+
const div = doc.createElement("div");
40+
div.setAttribute("style", "display: none;");
41+
doc.body.insertBefore(div, doc.body.firstChild);
42+
this.div = div;
43+
this.e = null;
44+
this.r = null;
45+
doc.addEventListener("mousemove", this, true);
46+
doc.addEventListener("click", this, true);
47+
}
4848

49-
TargetSelector.prototype.cleanup = function () {
50-
try {
51-
if (this.div) {
52-
if (this.div.parentNode) {
53-
this.div.parentNode.removeChild(this.div);
49+
cleanup() {
50+
try {
51+
if (this.div) {
52+
if (this.div.parentNode) {
53+
this.div.parentNode.removeChild(this.div);
54+
}
55+
this.div = null;
56+
}
57+
if (this.win) {
58+
const doc = this.win.document;
59+
doc.removeEventListener("mousemove", this, true);
60+
doc.removeEventListener("click", this, true);
61+
}
62+
} catch (e) {
63+
if (e != "TypeError: can't access dead object") {
64+
throw e;
5465
}
55-
this.div = null;
56-
}
57-
if (this.win) {
58-
const doc = this.win.document;
59-
doc.removeEventListener("mousemove", this, true);
60-
doc.removeEventListener("click", this, true);
6166
}
62-
} catch (e) {
63-
if (e != "TypeError: can't access dead object") {
64-
throw e;
67+
this.win = null;
68+
if (this.cleanupCallback) {
69+
this.cleanupCallback();
6570
}
6671
}
67-
this.win = null;
68-
if (this.cleanupCallback) {
69-
this.cleanupCallback();
70-
}
71-
};
7272

73-
TargetSelector.prototype.handleEvent = function (evt) {
74-
switch (evt.type) {
75-
case "mousemove":
76-
this.highlight(evt.target.ownerDocument, evt.clientX, evt.clientY);
77-
break;
78-
case "click":
79-
if (evt.button == 0 && this.e && this.callback) {
80-
this.callback(this.e, this.win);
81-
} //Right click would cancel the select
82-
evt.preventDefault();
83-
evt.stopPropagation();
84-
this.cleanup();
85-
break;
73+
handleEvent(evt) {
74+
switch (evt.type) {
75+
case "mousemove":
76+
this.highlight(evt.target.ownerDocument, evt.clientX, evt.clientY);
77+
break;
78+
case "click":
79+
if (evt.button == 0 && this.e && this.callback) {
80+
this.callback(this.e, this.win);
81+
} //Right click would cancel the select
82+
evt.preventDefault();
83+
evt.stopPropagation();
84+
this.cleanup();
85+
break;
86+
}
8687
}
87-
};
8888

89-
TargetSelector.prototype.highlight = function (doc, x, y) {
90-
if (doc) {
91-
const e = doc.elementFromPoint(x, y);
92-
if (e && e != this.e) {
93-
this.highlightElement(e);
89+
highlight(doc, x, y) {
90+
if (doc) {
91+
const e = doc.elementFromPoint(x, y);
92+
if (e && e != this.e) {
93+
this.highlightElement(e);
94+
}
9495
}
9596
}
96-
};
9797

98-
TargetSelector.prototype.highlightElement = function (element) {
99-
if (element && element != this.e) {
100-
this.e = element;
101-
} else {
102-
return;
103-
}
104-
const r = element.getBoundingClientRect();
105-
const or = this.r;
106-
if (r.left >= 0 && r.top >= 0 && r.width > 0 && r.height > 0) {
107-
if (or && r.top == or.top && r.left == or.left && r.width == or.width && r.height == or.height) {
98+
highlightElement(element) {
99+
if (element && element != this.e) {
100+
this.e = element;
101+
} else {
108102
return;
109103
}
110-
this.r = r;
111-
const style = "pointer-events: none; position: absolute; background-color: rgb(78, 171, 230); opacity: 0.4; border: 1px solid #0e0e0e; z-index: 100;";
112-
const pos = "top:" + (r.top + this.win.scrollY) + "px; left:" + (r.left + this.win.scrollX) + "px; width:" + r.width + "px; height:" + r.height + "px;";
113-
this.div.setAttribute("style", style + pos);
114-
} else if (or) {
115-
this.div.setAttribute("style", "display: none;");
104+
const r = element.getBoundingClientRect();
105+
const or = this.r;
106+
if (r.left >= 0 && r.top >= 0 && r.width > 0 && r.height > 0) {
107+
if (or && r.top == or.top && r.left == or.left && r.width == or.width && r.height == or.height) {
108+
return;
109+
}
110+
this.r = r;
111+
const style = "pointer-events: none; position: absolute; background-color: rgb(78, 171, 230); opacity: 0.4; border: 1px solid #0e0e0e; z-index: 100;";
112+
const pos = `top:${r.top + this.win.scrollY}px; left:${r.left + this.win.scrollX}px; width:${r.width}px; height:${r.height}px;`;
113+
this.div.setAttribute("style", style + pos);
114+
} else if (or) {
115+
this.div.setAttribute("style", "display: none;");
116+
}
116117
}
117-
};
118+
}
118119

119120
export default TargetSelector;

packages/selenium-ide/src/neo/IO/filesystem.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,11 @@ export function saveProject(_project) {
7070
}
7171

7272
function downloadProject(project) {
73-
return exportProject(project).then(code => {
74-
project.code = code;
75-
Object.assign(project, Manager.emitDependencies());
73+
return exportProject(project).then(snapshot => {
74+
if (snapshot) {
75+
project.snapshot = snapshot;
76+
Object.assign(project, Manager.emitDependencies());
77+
}
7678
return browser.downloads.download({
7779
filename: project.name + ".side",
7880
url: createBlob("application/json", beautify(JSON.stringify(project), { indent_size: 2 })),
@@ -84,7 +86,7 @@ function downloadProject(project) {
8486

8587
function exportProject(project) {
8688
return Manager.validatePluginExport(project).then(() => {
87-
return Selianize(project, { silenceErrors: true }).catch(err => {
89+
return Selianize(project, { silenceErrors: true, skipStdLibEmitting: true }).catch(err => {
8890
const markdown = ParseError(err && err.message || err);
8991
ModalState.showAlert({
9092
title: "Error saving project",

packages/selenium-side-runner/src/index.js

Lines changed: 47 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import program from "commander";
2525
import winston from "winston";
2626
import rimraf from "rimraf";
2727
import { js_beautify as beautify } from "js-beautify";
28+
import Selianize from "selianize";
2829
import Capabilities from "./capabilities";
2930
import Config from "./config";
3031
import metadata from "../package.json";
@@ -101,7 +102,7 @@ configuration.baseUrl = program.baseUrl ? program.baseUrl : configuration.baseUr
101102
let projectPath;
102103

103104
function runProject(project) {
104-
if (!project.code || project.version !== "1.0") {
105+
if (project.version !== "1.0") {
105106
return Promise.reject(new TypeError(`The project ${project.name} is of older format, open and save it again using the IDE.`));
106107
}
107108
projectPath = `side-suite-${project.name}`;
@@ -118,56 +119,59 @@ function runProject(project) {
118119
},
119120
dependencies: project.dependencies || {}
120121
}));
121-
const tests = project.code.tests.reduce((tests, test) => {
122-
return tests += test.code;
123-
}, "const tests = {};").concat("module.exports = tests;");
124-
writeJSFile(path.join(projectPath, "commons"), tests, ".js");
125-
project.code.suites.forEach(suite => {
126-
if (!suite.tests) {
127-
// not parallel
128-
const cleanup = suite.persistSession ? "" : "beforeEach(() => {vars = {};});afterEach(async () => (cleanup()));";
129-
writeJSFile(path.join(projectPath, suite.name), `// This file was generated using Selenium IDE\nconst tests = require("./commons.js");${suite.code}${cleanup}`);
130-
} else if (suite.tests.length) {
131-
fs.mkdirSync(path.join(projectPath, suite.name));
132-
// parallel suite
133-
suite.tests.forEach(test => {
134-
writeJSFile(path.join(projectPath, suite.name, test.name), `// This file was generated using Selenium IDE\nconst tests = require("../commons.js");${suite.code}${test.code}`);
135-
});
136-
}
137-
});
138-
winston.info(`Running ${project.name}`);
139122

140-
return new Promise((resolve, reject) => {
141-
let npmInstall;
142-
if (project.dependencies && Object.keys(project.dependencies).length) {
143-
npmInstall = new Promise((resolve, reject) => {
144-
const child = fork(require.resolve("./npm"), { cwd: path.join(process.cwd(), projectPath), stdio: "inherit" });
123+
return Selianize(project, { silenceErrors: true }, project.snapshot).then((code) => {
124+
const tests = code.tests.reduce((tests, test) => {
125+
return tests += test.code;
126+
}, "const tests = {};").concat("module.exports = tests;");
127+
writeJSFile(path.join(projectPath, "commons"), tests, ".js");
128+
code.suites.forEach(suite => {
129+
if (!suite.tests) {
130+
// not parallel
131+
const cleanup = suite.persistSession ? "" : "beforeEach(() => {vars = {};});afterEach(async () => (cleanup()));";
132+
writeJSFile(path.join(projectPath, suite.name), `// This file was generated using Selenium IDE\nconst tests = require("./commons.js");${code.globalConfig}${suite.code}${cleanup}`);
133+
} else if (suite.tests.length) {
134+
fs.mkdirSync(path.join(projectPath, suite.name));
135+
// parallel suite
136+
suite.tests.forEach(test => {
137+
writeJSFile(path.join(projectPath, suite.name, test.name), `// This file was generated using Selenium IDE\nconst tests = require("../commons.js");${code.globalConfig}${suite.code}${test.code}`);
138+
});
139+
}
140+
});
141+
winston.info(`Running ${project.name}`);
142+
143+
return new Promise((resolve, reject) => {
144+
let npmInstall;
145+
if (project.dependencies && Object.keys(project.dependencies).length) {
146+
npmInstall = new Promise((resolve, reject) => {
147+
const child = fork(require.resolve("./npm"), { cwd: path.join(process.cwd(), projectPath), stdio: "inherit" });
148+
child.on("exit", (code) => {
149+
if (code) {
150+
reject();
151+
} else {
152+
resolve();
153+
}
154+
});
155+
});
156+
} else {
157+
npmInstall = Promise.resolve();
158+
}
159+
npmInstall.then(() => {
160+
const child = fork(require.resolve("./child"), [
161+
"--testMatch", `{**/*${program.filter}*/*.test.js,**/*${program.filter}*.test.js}`
162+
].concat(program.maxWorkers ? ["-w", program.maxWorkers] : []), { cwd: path.join(process.cwd(), projectPath), stdio: "inherit" });
163+
145164
child.on("exit", (code) => {
165+
console.log("");
166+
rimraf.sync(projectPath);
146167
if (code) {
147168
reject();
148169
} else {
149170
resolve();
150171
}
151172
});
152-
});
153-
} else {
154-
npmInstall = Promise.resolve();
155-
}
156-
npmInstall.then(() => {
157-
const child = fork(require.resolve("./child"), [
158-
"--testMatch", `{**/*${program.filter}*/*.test.js,**/*${program.filter}*.test.js}`
159-
].concat(program.maxWorkers ? ["-w", program.maxWorkers] : []), { cwd: path.join(process.cwd(), projectPath), stdio: "inherit" });
160-
161-
child.on("exit", (code) => {
162-
console.log("");
163-
rimraf.sync(projectPath);
164-
if (code) {
165-
reject();
166-
} else {
167-
resolve();
168-
}
169-
});
170-
}).catch(reject);
173+
}).catch(reject);
174+
});
171175
});
172176
}
173177

packages/selianize/__tests__/command.spec.js

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
// specific language governing permissions and limitations
1616
// under the License.
1717

18-
import CommandEmitter from "../src/command";
18+
import CommandEmitter, { registerEmitter } from "../src/command";
1919
import { Commands } from "../../selenium-ide/src/neo/models/Command";
2020

2121
describe("command code emitter", () => {
@@ -41,7 +41,7 @@ describe("command code emitter", () => {
4141
target: "/",
4242
value: ""
4343
};
44-
return expect(CommandEmitter.emit(command)).resolves.toBe(`await driver.get(BASE_URL + "${command.target}");`);
44+
return expect(CommandEmitter.emit(command)).resolves.toBe(`await driver.get((new URL("${command.target}", BASE_URL)).href);`);
4545
});
4646
it("should emit `open` with absolute url", () => {
4747
const command = {
@@ -637,4 +637,49 @@ describe("command code emitter", () => {
637637
}).not.toThrow();
638638
});
639639
});
640+
it("should skip emitting stdlib command when skipStdLibEmitting is set", () => {
641+
const command = {
642+
command: "open",
643+
target: "/",
644+
value: ""
645+
};
646+
return expect(CommandEmitter.emit(command, { skipStdLibEmitting: true })).resolves.toEqual({ skipped: true });
647+
});
648+
it("should emit a snapshot for a non-stdlib command when skipStdLibEmitting is set", () => {
649+
const command = {
650+
command: "aNewCommand",
651+
target: "",
652+
value: ""
653+
};
654+
registerEmitter(command.command, () => ("new command code"));
655+
return expect(CommandEmitter.emit(command, { skipStdLibEmitting: true })).resolves.toBe("new command code");
656+
});
657+
it("should throw an error for an invalid non-stdlib command even when skipStdLibEmitting is set", () => {
658+
const command = {
659+
command: "errorCommand",
660+
target: "",
661+
value: ""
662+
};
663+
registerEmitter(command.command, () => {
664+
throw new Error("an error occurred");
665+
});
666+
return expect(CommandEmitter.emit(command, { skipStdLibEmitting: true })).rejects.toThrow("an error occurred");
667+
});
668+
it("should not throw an error for an unknown command when skipStdLibEmitting is set", () => {
669+
const command = {
670+
command: "doesntExist",
671+
target: "",
672+
value: ""
673+
};
674+
return expect(CommandEmitter.emit(command, { skipStdLibEmitting: true })).resolves.toEqual({ skipped: true });
675+
});
676+
it("should return the snapshot when an unknown command is being emitted with snapshot sent", () => {
677+
const command = {
678+
command: "doesntExist",
679+
target: "",
680+
value: ""
681+
};
682+
const snapshot = "this commands snapshot";
683+
return expect(CommandEmitter.emit(command, undefined, snapshot)).resolves.toBe(snapshot);
684+
});
640685
});

0 commit comments

Comments
 (0)