codeceptjs 2.1.3 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +125 -37
- package/README.md +15 -22
- package/bin/codecept.js +4 -1
- package/docs/acceptance.md +44 -1
- package/docs/advanced.md +1 -1
- package/docs/angular.md +6 -9
- package/docs/basics.md +388 -75
- package/docs/bdd.md +4 -3
- package/docs/best.md +1 -1
- package/docs/books.md +31 -0
- package/docs/build/Appium.js +215 -176
- package/docs/build/Nightmare.js +618 -489
- package/docs/build/Polly.js +189 -0
- package/docs/build/Protractor.js +747 -608
- package/docs/build/Puppeteer.js +914 -633
- package/docs/build/REST.js +1 -1
- package/docs/build/TestCafe.js +1835 -0
- package/docs/build/WebDriver.js +861 -805
- package/docs/build/WebDriverIO.js +616 -617
- package/docs/changelog.md +410 -316
- package/docs/commands.md +6 -6
- package/docs/community-helpers.md +2 -0
- package/docs/detox.md +235 -0
- package/docs/examples.md +23 -0
- package/docs/helpers/ApiDataFactory.md +11 -10
- package/docs/helpers/Appium.md +130 -61
- package/docs/helpers/Detox.md +579 -0
- package/docs/helpers/FileSystem.md +2 -1
- package/docs/helpers/Mochawesome.md +1 -0
- package/docs/helpers/Nightmare.md +348 -128
- package/docs/helpers/Polly.md +85 -0
- package/docs/helpers/Protractor.md +451 -184
- package/docs/helpers/Puppeteer-firefox.md +55 -0
- package/docs/helpers/Puppeteer.md +619 -183
- package/docs/helpers/REST.md +17 -16
- package/docs/helpers/SeleniumWebdriver.md +9 -8
- package/docs/helpers/TestCafe.md +1168 -0
- package/docs/helpers/WebDriver.md +600 -291
- package/docs/helpers/WebDriverIO.md +393 -278
- package/docs/helpers.md +37 -18
- package/docs/locators.md +2 -0
- package/docs/mobile-react-native-locators.md +64 -0
- package/docs/mobile.md +5 -0
- package/docs/plugins.md +54 -13
- package/docs/puppeteer.md +74 -26
- package/docs/quickstart.md +47 -12
- package/docs/react.md +67 -0
- package/docs/reports.md +1 -1
- package/docs/{webapi/_keys.mustache → shared/keys.mustache} +0 -0
- package/docs/shared/react.mustache +1 -0
- package/docs/testcafe.md +157 -0
- package/docs/videos.md +19 -0
- package/docs/webapi/amOnPage.mustache +1 -1
- package/docs/webapi/appendField.mustache +2 -2
- package/docs/webapi/attachFile.mustache +2 -2
- package/docs/webapi/checkOption.mustache +2 -2
- package/docs/webapi/clearCookie.mustache +1 -1
- package/docs/webapi/clearField.mustache +1 -1
- package/docs/webapi/click.mustache +2 -2
- package/docs/webapi/clickLink.mustache +3 -3
- package/docs/webapi/dontSee.mustache +6 -3
- package/docs/webapi/dontSeeCheckboxIsChecked.mustache +7 -1
- package/docs/webapi/dontSeeCookie.mustache +5 -1
- package/docs/webapi/dontSeeCurrentUrlEquals.mustache +6 -1
- package/docs/webapi/dontSeeElement.mustache +5 -1
- package/docs/webapi/dontSeeElementInDOM.mustache +5 -1
- package/docs/webapi/dontSeeInCurrentUrl.mustache +1 -1
- package/docs/webapi/dontSeeInField.mustache +7 -2
- package/docs/webapi/dontSeeInSource.mustache +5 -1
- package/docs/webapi/dontSeeInTitle.mustache +5 -1
- package/docs/webapi/doubleClick.mustache +2 -2
- package/docs/webapi/downloadFile.mustache +2 -2
- package/docs/webapi/dragAndDrop.mustache +2 -2
- package/docs/webapi/dragSlider.mustache +2 -2
- package/docs/webapi/executeAsyncScript.mustache +1 -1
- package/docs/webapi/executeScript.mustache +1 -1
- package/docs/webapi/fillField.mustache +2 -2
- package/docs/webapi/grabAttributeFrom.mustache +3 -2
- package/docs/webapi/grabBrowserLogs.mustache +3 -1
- package/docs/webapi/grabCookie.mustache +2 -1
- package/docs/webapi/grabCssPropertyFrom.mustache +3 -2
- package/docs/webapi/grabCurrentUrl.mustache +3 -1
- package/docs/webapi/grabDataFromPerformanceTiming.mustache +19 -0
- package/docs/webapi/grabHTMLFrom.mustache +2 -1
- package/docs/webapi/grabNumberOfOpenTabs.mustache +4 -2
- package/docs/webapi/grabNumberOfVisibleElements.mustache +3 -2
- package/docs/webapi/grabPageScrollPosition.mustache +3 -1
- package/docs/webapi/grabSource.mustache +3 -1
- package/docs/webapi/grabTextFrom.mustache +2 -1
- package/docs/webapi/grabTitle.mustache +3 -1
- package/docs/webapi/grabValueFrom.mustache +2 -1
- package/docs/webapi/moveCursorTo.mustache +3 -3
- package/docs/webapi/pressKey.mustache +1 -1
- package/docs/webapi/resizeWindow.mustache +2 -2
- package/docs/webapi/rightClick.mustache +2 -2
- package/docs/webapi/saveScreenshot.mustache +3 -3
- package/docs/webapi/say.mustache +2 -2
- package/docs/webapi/scrollPageToBottom.mustache +1 -1
- package/docs/webapi/scrollPageToTop.mustache +1 -1
- package/docs/webapi/scrollTo.mustache +3 -3
- package/docs/webapi/see.mustache +2 -2
- package/docs/webapi/seeAttributesOnElements.mustache +3 -3
- package/docs/webapi/seeCheckboxIsChecked.mustache +2 -1
- package/docs/webapi/seeCookie.mustache +1 -1
- package/docs/webapi/seeCssPropertiesOnElements.mustache +2 -2
- package/docs/webapi/seeCurrentUrlEquals.mustache +1 -1
- package/docs/webapi/seeElement.mustache +1 -1
- package/docs/webapi/seeElementInDOM.mustache +1 -1
- package/docs/webapi/seeInCurrentUrl.mustache +1 -1
- package/docs/webapi/seeInField.mustache +2 -2
- package/docs/webapi/seeInSource.mustache +1 -1
- package/docs/webapi/seeInTitle.mustache +5 -1
- package/docs/webapi/seeNumberOfElements.mustache +10 -0
- package/docs/webapi/seeNumberOfVisibleElements.mustache +2 -2
- package/docs/webapi/selectOption.mustache +2 -2
- package/docs/webapi/setCookie.mustache +1 -1
- package/docs/webapi/switchTo.mustache +6 -1
- package/docs/webapi/uncheckOption.mustache +2 -2
- package/docs/webapi/wait.mustache +1 -2
- package/docs/webapi/waitForDetached.mustache +3 -3
- package/docs/webapi/waitForElement.mustache +2 -2
- package/docs/webapi/waitForEnabled.mustache +1 -1
- package/docs/webapi/waitForFunction.mustache +3 -3
- package/docs/webapi/waitForInvisible.mustache +3 -3
- package/docs/webapi/waitForText.mustache +3 -3
- package/docs/webapi/waitForValue.mustache +3 -3
- package/docs/webapi/waitForVisible.mustache +3 -3
- package/docs/webapi/waitInUrl.mustache +2 -2
- package/docs/webapi/waitNumberOfVisibleElements.mustache +3 -3
- package/docs/webapi/waitToHide.mustache +3 -3
- package/docs/webapi/waitUntil.mustache +3 -3
- package/docs/webapi/waitUrlEquals.mustache +2 -2
- package/docs/webdriver.md +453 -0
- package/lib/codecept.js +11 -9
- package/lib/command/definitions.js +183 -30
- package/lib/command/gherkin/snippets.js +29 -9
- package/lib/command/init.js +31 -9
- package/lib/command/run-multiple.js +46 -59
- package/lib/command/utils.js +1 -1
- package/lib/container.js +30 -4
- package/lib/data/dataScenarioConfig.js +18 -0
- package/lib/helper/Appium.js +24 -24
- package/lib/helper/Nightmare.js +81 -84
- package/lib/helper/Polly.js +189 -0
- package/lib/helper/Protractor.js +96 -86
- package/lib/helper/Puppeteer.js +238 -113
- package/lib/helper/REST.js +1 -1
- package/lib/helper/TestCafe.js +1257 -0
- package/lib/helper/WebDriver.js +217 -277
- package/lib/helper/WebDriverIO.js +75 -75
- package/lib/helper/clientscripts/nightmare.js +8 -0
- package/lib/helper/extras/React.js +55 -0
- package/lib/helper/testcafe/testControllerHolder.js +42 -0
- package/lib/helper/testcafe/testcafe-utils.js +63 -0
- package/lib/history.js +39 -0
- package/lib/hooks.js +25 -1
- package/lib/interfaces/gherkin.js +17 -1
- package/lib/interfaces/scenarioConfig.js +2 -2
- package/lib/listener/config.js +3 -3
- package/lib/locator.js +6 -0
- package/lib/pause.js +22 -1
- package/lib/plugin/allure.js +63 -0
- package/lib/plugin/autoLogin.js +65 -16
- package/lib/plugin/puppeteerCoverage.js +6 -1
- package/lib/plugin/stepByStepReport.js +4 -3
- package/lib/scenario.js +23 -17
- package/lib/step.js +5 -2
- package/lib/ui.js +1 -1
- package/lib/utils.js +70 -20
- package/package.json +20 -19
- package/translations/de-DE.js +69 -0
- package/translations/index.js +1 -0
- package/docs/video.md +0 -26
|
@@ -9,23 +9,24 @@ const fs = require('fs');
|
|
|
9
9
|
const path = require('path');
|
|
10
10
|
|
|
11
11
|
const template = `
|
|
12
|
-
type ICodeceptCallback = (i
|
|
12
|
+
type ICodeceptCallback = (i?: CodeceptJS.{{I}}, current?:any{{callbackParams}}) => void;
|
|
13
13
|
|
|
14
14
|
declare class FeatureConfig {
|
|
15
|
-
retry(times:number): FeatureConfig
|
|
16
|
-
timeout(seconds:number): FeatureConfig
|
|
17
|
-
config(config:object): FeatureConfig
|
|
18
|
-
config(helperName:string, config:object): FeatureConfig
|
|
15
|
+
retry(times: number): FeatureConfig
|
|
16
|
+
timeout(seconds: number): FeatureConfig
|
|
17
|
+
config(config: object): FeatureConfig
|
|
18
|
+
config(helperName: string, config: object): FeatureConfig
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
declare class ScenarioConfig {
|
|
22
|
-
throws(err:any)
|
|
23
|
-
fails()
|
|
24
|
-
retry(times:number): ScenarioConfig
|
|
25
|
-
timeout(timeout:number): ScenarioConfig
|
|
26
|
-
inject(inject:object): ScenarioConfig
|
|
27
|
-
config(config:object): ScenarioConfig
|
|
28
|
-
config(helperName:string, config:object): ScenarioConfig
|
|
22
|
+
throws(err: any): ScenarioConfig;
|
|
23
|
+
fails(): ScenarioConfig;
|
|
24
|
+
retry(times: number): ScenarioConfig
|
|
25
|
+
timeout(timeout: number): ScenarioConfig
|
|
26
|
+
inject(inject: object): ScenarioConfig
|
|
27
|
+
config(config: object): ScenarioConfig
|
|
28
|
+
config(helperName: string, config: object): ScenarioConfig
|
|
29
|
+
injectDependencies(dependencies: { [key: string]: any }): ScenarioConfig
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
interface ILocator {
|
|
@@ -40,6 +41,134 @@ interface ILocator {
|
|
|
40
41
|
|
|
41
42
|
type LocatorOrString = string | ILocator | Locator;
|
|
42
43
|
|
|
44
|
+
declare class Container {
|
|
45
|
+
create(config: Object, opts: Object): void
|
|
46
|
+
plugins(name?: string): Object
|
|
47
|
+
support(name?: string): Object
|
|
48
|
+
helpers(name?: string): Object
|
|
49
|
+
translation(): Object
|
|
50
|
+
mocha(): Object
|
|
51
|
+
append(newContainer: Object): void
|
|
52
|
+
clear(newHelpers: Object, newSupport: Object, newPlugins: Object): void
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
declare class RecorderSession {
|
|
56
|
+
running: boolean
|
|
57
|
+
start(name: string): void
|
|
58
|
+
restore(name: string): void
|
|
59
|
+
catch(fn: CallableFunction): void
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
declare class Recorder {
|
|
63
|
+
retries: Object[]
|
|
64
|
+
start(): void
|
|
65
|
+
isRunning(): boolean
|
|
66
|
+
startUnlessRunning(): void
|
|
67
|
+
errHandler(fn: CallableFunction): void
|
|
68
|
+
reset(): void
|
|
69
|
+
session: RecorderSession
|
|
70
|
+
add(taskName, fn?: CallableFunction, force?: boolean, retry?: boolean): Promise<any>
|
|
71
|
+
retry(opts: Object): Promise<any>
|
|
72
|
+
catch(customErrFn: CallableFunction): Promise<any>
|
|
73
|
+
catchWithoutStop(customErrFn: CallableFunction ): Promise<any>
|
|
74
|
+
throw(err: Error): Promise<any>
|
|
75
|
+
saveFirstAsyncError(err: Error): void
|
|
76
|
+
getAsyncErr(): Promise<Error>
|
|
77
|
+
cleanAsyncErr(): void
|
|
78
|
+
stop():void
|
|
79
|
+
promise(): Promise<any>
|
|
80
|
+
scheduled(): string[]
|
|
81
|
+
toString(): string
|
|
82
|
+
add(hookName: string, fn: CallableFunction, force?: boolean): void
|
|
83
|
+
catch(customErrFn: CallableFunction)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
declare class CodeceptJSEvent {
|
|
87
|
+
dispatcher: EventEmitter
|
|
88
|
+
test: {
|
|
89
|
+
started: string
|
|
90
|
+
before: string
|
|
91
|
+
after: string
|
|
92
|
+
passed: string
|
|
93
|
+
failed: string
|
|
94
|
+
finished: string
|
|
95
|
+
}
|
|
96
|
+
suite: {
|
|
97
|
+
before: string,
|
|
98
|
+
after: string,
|
|
99
|
+
}
|
|
100
|
+
hook: {
|
|
101
|
+
started: string,
|
|
102
|
+
passed: string,
|
|
103
|
+
}
|
|
104
|
+
step: {
|
|
105
|
+
before: string,
|
|
106
|
+
after: string,
|
|
107
|
+
started: string,
|
|
108
|
+
passed: string,
|
|
109
|
+
failed: string,
|
|
110
|
+
finished: string,
|
|
111
|
+
}
|
|
112
|
+
all: {
|
|
113
|
+
before: string,
|
|
114
|
+
after: string,
|
|
115
|
+
result: string,
|
|
116
|
+
}
|
|
117
|
+
multiple: {
|
|
118
|
+
before: string,
|
|
119
|
+
after: string,
|
|
120
|
+
}
|
|
121
|
+
emit(event: string, param: string)
|
|
122
|
+
cleanDispatcher(): void
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
declare class Output {
|
|
126
|
+
colors: any
|
|
127
|
+
styles: {
|
|
128
|
+
error: any,
|
|
129
|
+
success: any,
|
|
130
|
+
scenario: any,
|
|
131
|
+
basic: any,
|
|
132
|
+
debug: any,
|
|
133
|
+
log: any,
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
print(msg: string): void
|
|
137
|
+
stepShift: number
|
|
138
|
+
level(level: number): number
|
|
139
|
+
process(process: string): string
|
|
140
|
+
debug(msg: string): void
|
|
141
|
+
log(msg: string): void
|
|
142
|
+
error(msg: string): void
|
|
143
|
+
success(msg: string): void
|
|
144
|
+
plugin(name: string, msg: string): void
|
|
145
|
+
step(step: any): void
|
|
146
|
+
suite: {
|
|
147
|
+
started: Function
|
|
148
|
+
}
|
|
149
|
+
test: {
|
|
150
|
+
started(test: string): void
|
|
151
|
+
passed(test: string): void
|
|
152
|
+
failed(test: string): void
|
|
153
|
+
skipped(test: string): void
|
|
154
|
+
}
|
|
155
|
+
scenario: {
|
|
156
|
+
started(test: string): void
|
|
157
|
+
passed(test: string): void
|
|
158
|
+
failed(test: string): void
|
|
159
|
+
}
|
|
160
|
+
say(message: string, color?: string): void
|
|
161
|
+
result(passed: number, failed: number, skipped: number, duration: string): void
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
declare class Config {
|
|
165
|
+
create(newConfig: Object): Object
|
|
166
|
+
load(configFile: string): Config
|
|
167
|
+
get(key: string, val: any): any
|
|
168
|
+
append(additionalConfig: Object): Object
|
|
169
|
+
reset(): Object
|
|
170
|
+
}
|
|
171
|
+
|
|
43
172
|
declare class Helper {
|
|
44
173
|
/** Abstract method to provide required config options */
|
|
45
174
|
static _config(): any;
|
|
@@ -110,24 +239,43 @@ declare const Scenario: {
|
|
|
110
239
|
only(title: string, callback: ICodeceptCallback): ScenarioConfig;
|
|
111
240
|
only(title: string, opts: {}, callback: ICodeceptCallback): ScenarioConfig;
|
|
112
241
|
}
|
|
242
|
+
declare interface IScenario {
|
|
243
|
+
Scenario(title: string, callback: ICodeceptCallback): ScenarioConfig;
|
|
244
|
+
Scenario(title: string, opts: {}, callback: ICodeceptCallback): ScenarioConfig;
|
|
245
|
+
}
|
|
113
246
|
declare function xScenario(title: string, callback: ICodeceptCallback): ScenarioConfig;
|
|
114
247
|
declare function xScenario(title: string, opts: {}, callback: ICodeceptCallback): ScenarioConfig;
|
|
115
|
-
declare
|
|
116
|
-
|
|
248
|
+
declare interface IData {
|
|
249
|
+
Scenario(title: string, callback: ICodeceptCallback): ScenarioConfig;
|
|
250
|
+
Scenario(title: string, opts: {}, callback: ICodeceptCallback): ScenarioConfig;
|
|
251
|
+
only: IScenario;
|
|
252
|
+
}
|
|
253
|
+
declare function Data(data: any): IData;
|
|
254
|
+
declare function xData(data: any): IData;
|
|
117
255
|
declare function Before(callback: ICodeceptCallback): void;
|
|
118
256
|
declare function BeforeSuite(callback: ICodeceptCallback): void;
|
|
119
257
|
declare function After(callback: ICodeceptCallback): void;
|
|
120
258
|
declare function AfterSuite(callback: ICodeceptCallback): void;
|
|
121
259
|
|
|
260
|
+
declare function inject(): {
|
|
261
|
+
{{injectPageObjects}}
|
|
262
|
+
};
|
|
122
263
|
declare function locate(selector: LocatorOrString): Locator;
|
|
123
264
|
declare function within(selector: LocatorOrString, callback: Function): Promise<any>;
|
|
124
|
-
declare function session(selector:
|
|
125
|
-
declare function session(selector:
|
|
265
|
+
declare function session(selector: LocatorOrString, callback: Function): Promise<any>;
|
|
266
|
+
declare function session(selector: LocatorOrString, config: any, callback: Function): Promise<any>;
|
|
126
267
|
declare function pause(): void;
|
|
268
|
+
declare function secret(secret: any): string;
|
|
127
269
|
|
|
128
270
|
declare const codeceptjs: any;
|
|
129
271
|
|
|
130
272
|
declare namespace CodeceptJS {
|
|
273
|
+
export const container: Container
|
|
274
|
+
export const recorder: Recorder
|
|
275
|
+
export const event: CodeceptJSEvent
|
|
276
|
+
export const output: Output
|
|
277
|
+
export const config: Config
|
|
278
|
+
|
|
131
279
|
export interface {{I}} {
|
|
132
280
|
{{methods}}
|
|
133
281
|
}
|
|
@@ -135,10 +283,12 @@ declare namespace CodeceptJS {
|
|
|
135
283
|
}
|
|
136
284
|
|
|
137
285
|
declare module "codeceptjs" {
|
|
138
|
-
|
|
286
|
+
export = CodeceptJS;
|
|
139
287
|
}
|
|
140
288
|
`;
|
|
141
289
|
|
|
290
|
+
const injectSupportTemplate = ' {{name}}: CodeceptJS.{{name}}';
|
|
291
|
+
|
|
142
292
|
const pageObjectTemplate = `
|
|
143
293
|
export interface {{name}} {
|
|
144
294
|
{{methods}}
|
|
@@ -151,6 +301,8 @@ module.exports = function (genPath, options) {
|
|
|
151
301
|
const config = getConfig(configFile);
|
|
152
302
|
if (!config) return;
|
|
153
303
|
|
|
304
|
+
const targetFolderPath = options.output && getTestRoot(options.output) || testsPath;
|
|
305
|
+
|
|
154
306
|
const codecept = new Codecept(config, {});
|
|
155
307
|
codecept.init(testsPath);
|
|
156
308
|
|
|
@@ -167,33 +319,34 @@ module.exports = function (genPath, options) {
|
|
|
167
319
|
|
|
168
320
|
const supports = container.support(); // return all support objects
|
|
169
321
|
const exportPageObjects = [];
|
|
322
|
+
const injectPageObjects = [];
|
|
170
323
|
const callbackParams = [];
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
324
|
+
// See #1795 and #1799, there's no obvious way to use for-in with Proxies
|
|
325
|
+
Reflect.ownKeys(supports).forEach((name) => {
|
|
326
|
+
if (name === 'I') {
|
|
327
|
+
injectPageObjects.push(injectSupportTemplate.replace(/{{name}}/g, String(name)));
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
callbackParams.push(`${String(name)}?:CodeceptJS.${String(name)}`);
|
|
174
331
|
const pageObject = supports[name];
|
|
175
332
|
const pageMethods = addAllMethodsInObject(pageObject, {}, []);
|
|
176
333
|
let pageObjectExport = pageObjectTemplate.replace('{{methods}}', pageMethods.join(''));
|
|
177
|
-
pageObjectExport = pageObjectExport.replace('{{name}}', name);
|
|
334
|
+
pageObjectExport = pageObjectExport.replace('{{name}}', String(name));
|
|
335
|
+
injectPageObjects.push(injectSupportTemplate.replace(/{{name}}/g, String(name)));
|
|
178
336
|
exportPageObjects.push(pageObjectExport);
|
|
179
|
-
}
|
|
337
|
+
});
|
|
180
338
|
|
|
181
339
|
let definitionsTemplate = template.replace('{{methods}}', methods.join(''));
|
|
182
340
|
definitionsTemplate = definitionsTemplate.replace('{{exportPageObjects}}', exportPageObjects.join('\n'));
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
} else {
|
|
186
|
-
definitionsTemplate = definitionsTemplate.replace('{{callbackParams}}', '');
|
|
187
|
-
}
|
|
341
|
+
definitionsTemplate = definitionsTemplate.replace('{{injectPageObjects}}', injectPageObjects.join('\n'));
|
|
342
|
+
definitionsTemplate = definitionsTemplate.replace('{{callbackParams}}', `, ${[...callbackParams, '...args: any'].join(', ')}`);
|
|
188
343
|
if (translations) {
|
|
189
344
|
definitionsTemplate = definitionsTemplate.replace(/\{\{I\}\}/g, translations.I);
|
|
190
345
|
}
|
|
191
346
|
|
|
192
|
-
fs.writeFileSync(path.join(
|
|
347
|
+
fs.writeFileSync(path.join(targetFolderPath, 'steps.d.ts'), definitionsTemplate);
|
|
193
348
|
output.print('TypeScript Definitions provide autocompletion in Visual Studio Code and other IDEs');
|
|
194
349
|
output.print('Definitions were generated in steps.d.ts');
|
|
195
|
-
output.print('Load them by adding at the top of a test file:');
|
|
196
|
-
output.print(output.colors.grey('\n/// <reference path="./steps.d.ts" />'));
|
|
197
350
|
};
|
|
198
351
|
|
|
199
352
|
function addAllMethodsInObject(supportObj, actions, methods, translations) {
|
|
@@ -8,6 +8,7 @@ const { Parser } = require('gherkin');
|
|
|
8
8
|
const glob = require('glob');
|
|
9
9
|
const fsPath = require('path');
|
|
10
10
|
const fs = require('fs');
|
|
11
|
+
const escapeStringRegexp = require('escape-string-regexp');
|
|
11
12
|
|
|
12
13
|
const parser = new Parser();
|
|
13
14
|
parser.stopAtFirstError = false;
|
|
@@ -30,13 +31,17 @@ module.exports = function (genPath, options) {
|
|
|
30
31
|
output.error('No gherkin steps defined in config. Exiting');
|
|
31
32
|
process.exit(1);
|
|
32
33
|
}
|
|
33
|
-
if (!config.gherkin.features) {
|
|
34
|
+
if (!options.feature && !config.gherkin.features) {
|
|
34
35
|
output.error('No gherkin features defined in config. Exiting');
|
|
35
36
|
process.exit(1);
|
|
36
37
|
}
|
|
38
|
+
if (options.path && !config.gherkin.steps.includes(options.path)) {
|
|
39
|
+
output.error(`You must include ${options.path} to the gherkin steps in your config file`);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
37
42
|
|
|
38
43
|
const files = [];
|
|
39
|
-
glob.sync(config.gherkin.features, { cwd: global.codecept_dir }).forEach((file) => {
|
|
44
|
+
glob.sync(options.feature || config.gherkin.features, { cwd: options.feature ? '.' : global.codecept_dir }).forEach((file) => {
|
|
40
45
|
if (!fsPath.isAbsolute(file)) {
|
|
41
46
|
file = fsPath.join(global.codecept_dir, file);
|
|
42
47
|
}
|
|
@@ -58,11 +63,21 @@ module.exports = function (genPath, options) {
|
|
|
58
63
|
try {
|
|
59
64
|
matchStep(step.text);
|
|
60
65
|
} catch (err) {
|
|
61
|
-
let stepLine
|
|
62
|
-
|
|
63
|
-
.
|
|
64
|
-
|
|
65
|
-
|
|
66
|
+
let stepLine;
|
|
67
|
+
if (/[{}()/]/.test(step.text)) {
|
|
68
|
+
stepLine = escapeStringRegexp(step.text)
|
|
69
|
+
.replace(/\//g, '\\/')
|
|
70
|
+
.replace(/\"(.*?)\"/g, '"(.*?)"')
|
|
71
|
+
.replace(/(\d+\\\.\d+)/, '(\\d+\\.\\d+)')
|
|
72
|
+
.replace(/ (\d+) /, ' (\\d+) ');
|
|
73
|
+
stepLine = Object.assign(stepLine, { type: step.keyword.trim(), location: step.location, regexp: true });
|
|
74
|
+
} else {
|
|
75
|
+
stepLine = step.text
|
|
76
|
+
.replace(/\"(.*?)\"/g, '{string}')
|
|
77
|
+
.replace(/(\d+\.\d+)/, '{float}')
|
|
78
|
+
.replace(/ (\d+) /, ' {int} ');
|
|
79
|
+
stepLine = Object.assign(stepLine, { type: step.keyword.trim(), location: step.location, regexp: false });
|
|
80
|
+
}
|
|
66
81
|
newSteps.push(stepLine);
|
|
67
82
|
}
|
|
68
83
|
}
|
|
@@ -81,7 +96,12 @@ module.exports = function (genPath, options) {
|
|
|
81
96
|
|
|
82
97
|
files.forEach(file => parseFile(file));
|
|
83
98
|
|
|
84
|
-
let stepFile = config.gherkin.steps[0];
|
|
99
|
+
let stepFile = options.path || config.gherkin.steps[0];
|
|
100
|
+
if (!fs.existsSync(stepFile)) {
|
|
101
|
+
output.error('Please enter a valid step file path ', stepFile);
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
|
|
85
105
|
if (!fsPath.isAbsolute(stepFile)) {
|
|
86
106
|
stepFile = fsPath.join(global.codecept_dir, stepFile);
|
|
87
107
|
}
|
|
@@ -90,7 +110,7 @@ module.exports = function (genPath, options) {
|
|
|
90
110
|
.filter((value, index, self) => self.indexOf(value) === index)
|
|
91
111
|
.map((step) => {
|
|
92
112
|
return `
|
|
93
|
-
${step.type}('${step}', () => {
|
|
113
|
+
${step.type}(${step.regexp ? '/^' : "'"}${step}${step.regexp ? '$/' : "'"}, () => {
|
|
94
114
|
// From "${step.file}" ${JSON.stringify(step.location)}
|
|
95
115
|
throw new Error('Not implemented yet');
|
|
96
116
|
});`;
|
package/lib/command/init.js
CHANGED
|
@@ -4,7 +4,8 @@ const fs = require('fs');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const { fileExists, beautify } = require('../utils');
|
|
6
6
|
const inquirer = require('inquirer');
|
|
7
|
-
const getTestRoot = require('./utils')
|
|
7
|
+
const { getTestRoot } = require('./utils');
|
|
8
|
+
const generateDefinitions = require('./definitions');
|
|
8
9
|
const isLocal = require('../utils').installedLocally();
|
|
9
10
|
const mkdirp = require('mkdirp');
|
|
10
11
|
const { inspect } = require('util');
|
|
@@ -18,7 +19,7 @@ const defaultConfig = {
|
|
|
18
19
|
mocha: {},
|
|
19
20
|
};
|
|
20
21
|
|
|
21
|
-
const helpers = ['WebDriver', '
|
|
22
|
+
const helpers = ['WebDriver', 'Puppeteer', 'TestCafe', 'Protractor', 'Nightmare', 'Appium'];
|
|
22
23
|
const translations = Object.keys(require('../../translations'));
|
|
23
24
|
|
|
24
25
|
const noTranslation = 'English (no localization)';
|
|
@@ -123,7 +124,7 @@ module.exports = function (initPath) {
|
|
|
123
124
|
// append file mask to the end of tests
|
|
124
125
|
if (!config.tests.match(/\*(.*?)$/)) {
|
|
125
126
|
config.tests = `${config.tests.replace(/\/+$/, '')}/*_test.js`;
|
|
126
|
-
console.
|
|
127
|
+
console.print(`Adding default test mask: ${config.tests}`);
|
|
127
128
|
}
|
|
128
129
|
|
|
129
130
|
if (result.translation !== noTranslation) config.translation = result.translation;
|
|
@@ -158,28 +159,48 @@ module.exports = function (initPath) {
|
|
|
158
159
|
}
|
|
159
160
|
fs.writeFileSync(stepFile, defaultActor);
|
|
160
161
|
config.include.I = result.steps_file;
|
|
161
|
-
|
|
162
|
+
print(`Steps file created at ${stepFile}`);
|
|
162
163
|
}
|
|
163
164
|
|
|
164
165
|
fs.writeFileSync(configFile, beautify(`exports.config = ${inspect(config, false, 4, false)}`), 'utf-8');
|
|
165
|
-
|
|
166
|
+
print(`Config created at ${configFile}`);
|
|
166
167
|
|
|
167
168
|
if (config.output) {
|
|
168
169
|
if (!fileExists(config.output)) {
|
|
169
170
|
mkdirp.sync(path.join(testsPath, config.output));
|
|
170
|
-
|
|
171
|
+
print(`Directory for temporary output files created at '${config.output}'`);
|
|
171
172
|
} else {
|
|
172
173
|
print(`Directory for temporary output files is already created at '${config.output}'`);
|
|
173
174
|
}
|
|
174
175
|
}
|
|
175
|
-
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
const jsconfig = {
|
|
179
|
+
compilerOption: {
|
|
180
|
+
allowJs: true,
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
const jsconfigJson = beautify(JSON.stringify(jsconfig));
|
|
184
|
+
const jsconfigFile = path.join(testsPath, 'jsconfig.json');
|
|
185
|
+
if (fileExists(jsconfigFile)) {
|
|
186
|
+
print(`jsconfig.json has already exists at ${jsconfigFile}`);
|
|
187
|
+
} else {
|
|
188
|
+
fs.writeFileSync(jsconfigFile, jsconfigJson);
|
|
189
|
+
print(`Intellisense enabled in ${jsconfigFile}`);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
generateDefinitions(testsPath, {});
|
|
193
|
+
|
|
194
|
+
print('');
|
|
195
|
+
success(' Almost done! Next step:');
|
|
196
|
+
success(' Create your first test by executing `npx codeceptjs gt` command ');
|
|
176
197
|
|
|
177
198
|
if (packages) {
|
|
178
199
|
print('\n--');
|
|
179
200
|
if (isLocal) {
|
|
180
|
-
|
|
201
|
+
success(`Please install dependent packages locally: ${colors.bold(`npm install --save-dev ${packages.join(' ')}`)}`);
|
|
181
202
|
} else {
|
|
182
|
-
|
|
203
|
+
success(`Please install dependent packages globally: [sudo] ${colors.bold(`npm install -g ${packages.join(' ')}`)}`);
|
|
183
204
|
}
|
|
184
205
|
}
|
|
185
206
|
};
|
|
@@ -194,6 +215,7 @@ module.exports = function (initPath) {
|
|
|
194
215
|
config.helpers[helperName][configName] = helperResult[key];
|
|
195
216
|
});
|
|
196
217
|
|
|
218
|
+
print('');
|
|
197
219
|
finish();
|
|
198
220
|
});
|
|
199
221
|
});
|
|
@@ -3,10 +3,12 @@ const {
|
|
|
3
3
|
} = require('./utils');
|
|
4
4
|
const fork = require('child_process').fork;
|
|
5
5
|
const path = require('path');
|
|
6
|
+
const crypto = require('crypto');
|
|
6
7
|
const runHook = require('../hooks');
|
|
7
8
|
const event = require('../event');
|
|
8
9
|
const collection = require('./run-multiple/collection');
|
|
9
10
|
const clearString = require('../utils').clearString;
|
|
11
|
+
const replaceValueDeep = require('../utils').replaceValueDeep;
|
|
10
12
|
|
|
11
13
|
const runner = path.join(__dirname, '/../../bin/codecept');
|
|
12
14
|
let config;
|
|
@@ -61,48 +63,52 @@ module.exports = function (selectedRuns, options) {
|
|
|
61
63
|
fail('No runs provided. Use --all option to run all configured runs');
|
|
62
64
|
}
|
|
63
65
|
|
|
64
|
-
const done = () =>
|
|
65
|
-
|
|
66
|
+
const done = () => {
|
|
67
|
+
event.emit(event.multiple.before, null);
|
|
68
|
+
if (options.config) { // update paths to config path
|
|
69
|
+
if (config.tests) {
|
|
70
|
+
config.tests = path.resolve(testRoot, config.tests);
|
|
71
|
+
}
|
|
72
|
+
if (config.gherkin && config.gherkin.features) {
|
|
73
|
+
config.gherkin.features = path.resolve(testRoot, config.gherkin.features);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
66
76
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if (config.gherkin && config.gherkin.features) {
|
|
70
|
-
config.gherkin.features = path.resolve(testRoot, config.gherkin.features);
|
|
77
|
+
if (options.features) {
|
|
78
|
+
config.tests = '';
|
|
71
79
|
}
|
|
72
|
-
}
|
|
73
80
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
81
|
+
if (options.tests && config.gherkin) {
|
|
82
|
+
config.gherkin.features = '';
|
|
83
|
+
}
|
|
77
84
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
85
|
+
const childProcessesPromise = new Promise((resolve, reject) => {
|
|
86
|
+
processesDone = resolve;
|
|
87
|
+
});
|
|
81
88
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
89
|
+
const runsToExecute = [];
|
|
90
|
+
collection.createRuns(selectedRuns, config).forEach((run) => {
|
|
91
|
+
const runName = run.getOriginalName() || run.getName();
|
|
92
|
+
const runConfig = run.getConfig();
|
|
93
|
+
runsToExecute.push(executeRun(runName, runConfig));
|
|
94
|
+
});
|
|
85
95
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const runConfig = run.getConfig();
|
|
90
|
-
runsToExecute.push(executeRun(runName, runConfig));
|
|
91
|
-
});
|
|
96
|
+
if (!runsToExecute.length) {
|
|
97
|
+
fail('Nothing scheduled for execution');
|
|
98
|
+
}
|
|
92
99
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
100
|
+
// Execute all forks
|
|
101
|
+
totalSubprocessCount = runsToExecute.length;
|
|
102
|
+
runsToExecute.forEach(runToExecute => runToExecute.call(this));
|
|
96
103
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
104
|
+
return childProcessesPromise.then(() => {
|
|
105
|
+
// fire hook
|
|
106
|
+
const done = () => event.emit(event.multiple.after, null);
|
|
107
|
+
runHook(config.teardownAll, done, 'teardownAll');
|
|
108
|
+
});
|
|
109
|
+
};
|
|
100
110
|
|
|
101
|
-
|
|
102
|
-
// fire hook
|
|
103
|
-
const done = () => event.emit(event.multiple.after, null);
|
|
104
|
-
runHook(config.teardownAll, done, 'teardownAll');
|
|
105
|
-
});
|
|
111
|
+
runHook(config.bootstrapAll, done, 'bootstrapAll');
|
|
106
112
|
};
|
|
107
113
|
|
|
108
114
|
function executeRun(runName, runConfig) {
|
|
@@ -114,23 +120,25 @@ function executeRun(runName, runConfig) {
|
|
|
114
120
|
const browserName = browserConfig.browser;
|
|
115
121
|
|
|
116
122
|
for (const key in browserConfig) {
|
|
117
|
-
overriddenConfig.helpers =
|
|
123
|
+
overriddenConfig.helpers = replaceValueDeep(overriddenConfig.helpers, key, browserConfig[key]);
|
|
118
124
|
}
|
|
119
125
|
|
|
120
126
|
let outputDir = `${runName}_`;
|
|
121
127
|
if (browserConfig.outputName) {
|
|
122
128
|
outputDir += typeof browserConfig.outputName === 'function' ? browserConfig.outputName() : browserConfig.outputName;
|
|
123
129
|
} else {
|
|
124
|
-
|
|
130
|
+
const hash = crypto.createHash('md5');
|
|
131
|
+
hash.update(JSON.stringify(browserConfig));
|
|
132
|
+
outputDir += hash.digest('hex');
|
|
125
133
|
}
|
|
126
134
|
outputDir += `_${runId}`;
|
|
127
135
|
|
|
128
136
|
outputDir = clearString(outputDir);
|
|
129
137
|
|
|
130
138
|
// tweaking default output directories and for mochawesome
|
|
131
|
-
overriddenConfig =
|
|
132
|
-
overriddenConfig =
|
|
133
|
-
overriddenConfig =
|
|
139
|
+
overriddenConfig = replaceValueDeep(overriddenConfig, 'output', path.join(config.output, outputDir));
|
|
140
|
+
overriddenConfig = replaceValueDeep(overriddenConfig, 'reportDir', path.join(config.output, outputDir));
|
|
141
|
+
overriddenConfig = replaceValueDeep(overriddenConfig, 'mochaFile', path.join(config.output, outputDir, `${browserName}_report.xml`));
|
|
134
142
|
|
|
135
143
|
// override tests configuration
|
|
136
144
|
if (overriddenConfig.tests) {
|
|
@@ -178,24 +186,3 @@ function executeRun(runName, runConfig) {
|
|
|
178
186
|
return onProcessEnd(1);
|
|
179
187
|
});
|
|
180
188
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* search key in object recursive and replace value in it
|
|
185
|
-
*/
|
|
186
|
-
function replaceValue(obj, key, value) {
|
|
187
|
-
if (!obj) return;
|
|
188
|
-
if (obj instanceof Array) {
|
|
189
|
-
for (const i in obj) {
|
|
190
|
-
replaceValue(obj[i], key, value);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
if (obj[key]) obj[key] = value;
|
|
194
|
-
if (typeof obj === 'object' && obj !== null) {
|
|
195
|
-
const children = Object.keys(obj);
|
|
196
|
-
for (let childIndex = 0; childIndex < children.length; childIndex++) {
|
|
197
|
-
replaceValue(obj[children[childIndex]], key, value);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
return obj;
|
|
201
|
-
}
|
package/lib/command/utils.js
CHANGED
package/lib/container.js
CHANGED
|
@@ -167,7 +167,7 @@ function createSupportObjects(config) {
|
|
|
167
167
|
objects[name] = {}; // placeholders
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
if (!
|
|
170
|
+
if (!config.I) {
|
|
171
171
|
objects.I = require('./actor')();
|
|
172
172
|
|
|
173
173
|
if (container.translation.I !== 'I') {
|
|
@@ -177,7 +177,7 @@ function createSupportObjects(config) {
|
|
|
177
177
|
|
|
178
178
|
container.support = objects;
|
|
179
179
|
|
|
180
|
-
|
|
180
|
+
function lazyLoad(name) {
|
|
181
181
|
let newObj = getSupportObject(config, name);
|
|
182
182
|
try {
|
|
183
183
|
if (typeof newObj === 'function') {
|
|
@@ -188,8 +188,9 @@ function createSupportObjects(config) {
|
|
|
188
188
|
} catch (err) {
|
|
189
189
|
throw new Error(`Initialization failed for ${name}: ${newObj}\n${err.message}`);
|
|
190
190
|
}
|
|
191
|
-
|
|
191
|
+
return newObj;
|
|
192
192
|
}
|
|
193
|
+
|
|
193
194
|
const asyncWrapper = function (f) {
|
|
194
195
|
return function () {
|
|
195
196
|
return f.apply(this, arguments).catch((e) => {
|
|
@@ -209,7 +210,32 @@ function createSupportObjects(config) {
|
|
|
209
210
|
});
|
|
210
211
|
});
|
|
211
212
|
|
|
212
|
-
return
|
|
213
|
+
return new Proxy({}, {
|
|
214
|
+
has(target, key) {
|
|
215
|
+
return key in config;
|
|
216
|
+
},
|
|
217
|
+
ownKeys() {
|
|
218
|
+
return Reflect.ownKeys(config);
|
|
219
|
+
},
|
|
220
|
+
get(target, key) {
|
|
221
|
+
// configured but not in support object, yet: load the module
|
|
222
|
+
if (key in objects && !(key in target)) {
|
|
223
|
+
// load default I
|
|
224
|
+
if (key in objects && !(key in config)) {
|
|
225
|
+
return target[key] = objects[key];
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// load new object
|
|
229
|
+
const object = lazyLoad(key);
|
|
230
|
+
// check that object is a real object and not an array
|
|
231
|
+
if (Object.prototype.toString.call(object) === '[object Object]') {
|
|
232
|
+
return target[key] = Object.assign(objects[key], object);
|
|
233
|
+
}
|
|
234
|
+
target[key] = object;
|
|
235
|
+
}
|
|
236
|
+
return target[key];
|
|
237
|
+
},
|
|
238
|
+
});
|
|
213
239
|
}
|
|
214
240
|
|
|
215
241
|
function createPlugins(config, options = {}) {
|