Skip to content

Commit 0c40f71

Browse files
authored
Merge pull request #310 from SeleniumHQ/snapshot-plugins
snapshot plugins commands, to reduce clutter in the project
2 parents 388b35e + f9a3b84 commit 0c40f71

File tree

20 files changed

+457
-240
lines changed

20 files changed

+457
-240
lines changed

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/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"js-yaml": "^3.10.0",
3636
"rimraf": "^2.6.2",
3737
"selenium-webdriver": "^3.6.0",
38+
"selianize": "*",
3839
"winston": "^2.4.0"
3940
},
4041
"devDependencies": {

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
});

packages/selianize/__tests__/configuration.spec.js

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,30 @@ describe("configuration code emitter", () => {
2222
const project = {
2323
url: "http://www.seleniumhq.org"
2424
};
25-
return expect(ConfigurationEmitter.emit(project)).resolves.toBe(`global.BASE_URL = configuration.baseUrl || '${project.url}';let vars = {};`);
25+
return expect(ConfigurationEmitter.emit(project)).resolves.toBe(`global.URL = require('url').URL;global.BASE_URL = configuration.baseUrl || '${project.url}';let vars = {};`);
26+
});
27+
it("should skip emitting project configuration when skipStdLibEmitting is set and there are no config hooks", () => {
28+
const project = {
29+
url: "http://www.seleniumhq.org"
30+
};
31+
return expect(ConfigurationEmitter.emit(project, { skipStdLibEmitting: true })).resolves.toEqual({
32+
skipped: true
33+
});
34+
});
35+
it("should append the configuration snapshot to the config", () => {
36+
const project = {
37+
url: "http://www.seleniumhq.org"
38+
};
39+
const snapshot = "extra global config";
40+
return expect(ConfigurationEmitter.emit(project, undefined, snapshot)).resolves.toBe(`global.URL = require('url').URL;global.BASE_URL = configuration.baseUrl || '${project.url}';let vars = {};${snapshot}`);
41+
});
42+
it("should emit a snapshot of the hooks when skipStdLibEmitting is set", () => {
43+
const project = {
44+
url: "http://www.seleniumhq.org"
45+
};
46+
ConfigurationEmitter.registerHook(() => ("some config code"));
47+
return expect(ConfigurationEmitter.emit(project, { skipStdLibEmitting: true })).resolves.toEqual({
48+
snapshot: "some config code"
49+
});
2650
});
2751
});

packages/selianize/__tests__/index.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ describe("Selenium code serializer", () => {
2929
const hook = jest.fn();
3030
hook.mockReturnValue(Promise.resolve("some code the the top"));
3131
RegisterConfigurationHook(hook);
32-
return expect((await Selianize(project)).suites[0].code).toMatch(/some code the the top/);
32+
return expect((await Selianize(project)).globalConfig).toMatch(/some code the the top/);
3333
});
3434
it("should register a suite emitter hook", async () => {
3535
const project = JSON.parse(fs.readFileSync(path.join(__dirname, "test-files", "project-3-single-test.side")));

packages/selianize/__tests__/suite.spec.js

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ describe("suite emitter", () => {
2626
timeout: "30",
2727
tests: []
2828
};
29-
return expect(SuiteEmitter.emit(suite, {})).resolves.toBe(`jest.setTimeout(30000);describe("${suite.name}", () => {});`);
29+
return expect(SuiteEmitter.emit(suite, {})).resolves.toEqual({
30+
code: `jest.setTimeout(30000);describe("${suite.name}", () => {});`
31+
});
3032
});
3133
it("should emit a suite with a single empty test", async () => {
3234
const tests = {
@@ -44,7 +46,9 @@ describe("suite emitter", () => {
4446
};
4547
return expect(SuiteEmitter.emit(suite, {
4648
1: await TestCaseEmitter.emit(tests["1"])
47-
})).resolves.toBe(`jest.setTimeout(30000);describe("${suite.name}", () => {it("${tests["1"].name}", async () => {await tests.example_test_case(driver, vars);await driver.getTitle().then(title => {expect(title).toBeDefined();});});});`);
49+
})).resolves.toEqual({
50+
code: `jest.setTimeout(30000);describe("${suite.name}", () => {it("${tests["1"].name}", async () => {await tests.example_test_case(driver, vars);await driver.getTitle().then(title => {expect(title).toBeDefined();});});});`
51+
});
4852
});
4953
it("should emit a suite with multiple empty tests", async () => {
5054
const tests = {
@@ -74,7 +78,9 @@ describe("suite emitter", () => {
7478
1: await TestCaseEmitter.emit(tests["1"]),
7579
2: await TestCaseEmitter.emit(tests["2"]),
7680
3: await TestCaseEmitter.emit(tests["3"])
77-
})).resolves.toBe(`jest.setTimeout(30000);describe("${suite.name}", () => {it("${tests["1"].name}", async () => {await tests.example_test_case(driver, vars);await driver.getTitle().then(title => {expect(title).toBeDefined();});});it("${tests["2"].name}", async () => {await tests.second_test_case(driver, vars);await driver.getTitle().then(title => {expect(title).toBeDefined();});});it("${tests["3"].name}", async () => {await tests.third_test_case(driver, vars);await driver.getTitle().then(title => {expect(title).toBeDefined();});});});`);
81+
})).resolves.toEqual({
82+
code: `jest.setTimeout(30000);describe("${suite.name}", () => {it("${tests["1"].name}", async () => {await tests.example_test_case(driver, vars);await driver.getTitle().then(title => {expect(title).toBeDefined();});});it("${tests["2"].name}", async () => {await tests.second_test_case(driver, vars);await driver.getTitle().then(title => {expect(title).toBeDefined();});});it("${tests["3"].name}", async () => {await tests.third_test_case(driver, vars);await driver.getTitle().then(title => {expect(title).toBeDefined();});});});`
83+
});
7884
});
7985
it("should emit a parallel suite", async () => {
8086
const tests = {
@@ -119,4 +125,46 @@ describe("suite emitter", () => {
119125
code: `jest.setTimeout(30000);test("${tests["3"].name}", async () => {await tests.third_test_case(driver, vars);await driver.getTitle().then(title => {expect(title).toBeDefined();});});`
120126
}]);
121127
});
128+
it("should skip emitting when skipStdLibEmitting is set and no hooks are registered", () => {
129+
const suite = {
130+
id: "1",
131+
name: "example suite",
132+
timeout: "30",
133+
tests: []
134+
};
135+
return expect(SuiteEmitter.emit(suite, {}, { skipStdLibEmitting: true })).resolves.toEqual({ skipped: true });
136+
});
137+
it("should emit a snapshot of the hooks when skipStdLibEmitting is set and hooks are registered", () => {
138+
const suite = {
139+
id: "1",
140+
name: "example suite",
141+
timeout: "30",
142+
tests: []
143+
};
144+
SuiteEmitter.registerHook(() => ({
145+
beforeAll: "beforeAll code",
146+
before: "before code",
147+
after: "after code",
148+
afterAll: "afterAll code"
149+
}));
150+
return expect(SuiteEmitter.emit(suite, {}, { skipStdLibEmitting: true })).resolves.toEqual({
151+
snapshot: {
152+
hook: "beforeAll(async () => {beforeAll code});beforeEach(async () => {before code});afterEach(async () => {after code});afterAll(async () => {afterAll code});"
153+
}
154+
});
155+
});
156+
it("should append the snapshot to the normal hooks", () => {
157+
const suite = {
158+
id: "1",
159+
name: "example suite",
160+
timeout: "30",
161+
tests: []
162+
};
163+
const snapshot = {
164+
hook: "hook results"
165+
};
166+
return expect(SuiteEmitter.emit(suite, {}, undefined, snapshot)).resolves.toEqual({
167+
code: "jest.setTimeout(30000);describe(\"example suite\", () => {beforeAll(async () => {beforeAll code});beforeEach(async () => {before code});afterEach(async () => {after code});afterAll(async () => {afterAll code});hook results});"
168+
});
169+
});
122170
});

0 commit comments

Comments
 (0)