Skip to content
Merged
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
152 changes: 148 additions & 4 deletions lib/command/definitions.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const fs = require('fs');
const path = require('path');

const template = `
type ICodeceptCallback = (i: CodeceptJS.{{I}}{{callbackParams}}) => void;
type ICodeceptCallback = (i?: CodeceptJS.{{I}}, current?:any{{callbackParams}}) => void;

declare class FeatureConfig {
retry(times: number): FeatureConfig
Expand Down Expand Up @@ -41,6 +41,134 @@ interface ILocator {

type LocatorOrString = string | ILocator | Locator;

declare class Container {
create(config: Object, opts: Object): void
plugins(name?: string): Object
support(name?: string): Object
helpers(name?: string): Object
translation(): Object
mocha(): Object
append(newContainer: Object): void
clear(newHelpers: Object, newSupport: Object, newPlugins: Object): void
}

declare class RecorderSession {
running: boolean
start(name: string): void
restore(name: string): void
catch(fn: CallableFunction): void
}

declare class Recorder {
retries: Object[]
start(): void
isRunning(): boolean
startUnlessRunning(): void
errHandler(fn: CallableFunction): void
reset(): void
session: RecorderSession
add(taskName, fn?: CallableFunction, force?: boolean, retry?: boolean): Promise<any>
retry(opts: Object): Promise<any>
catch(customErrFn: CallableFunction): Promise<any>
catchWithoutStop(customErrFn: CallableFunction ): Promise<any>
throw(err: Error): Promise<any>
saveFirstAsyncError(err: Error): void
getAsyncErr(): Promise<Error>
cleanAsyncErr(): void
stop():void
promise(): Promise<any>
scheduled(): string[]
toString(): string
add(hookName: string, fn: CallableFunction, force?: boolean): void
catch(customErrFn: CallableFunction)
}

declare class CodeceptJSEvent {
dispatcher: EventEmitter
test: {
started: string
before: string
after: string
passed: string
failed: string
finished: string
}
suite: {
before: string,
after: string,
}
hook: {
started: string,
passed: string,
}
step: {
before: string,
after: string,
started: string,
passed: string,
failed: string,
finished: string,
}
all: {
before: string,
after: string,
result: string,
}
multiple: {
before: string,
after: string,
}
emit(event: string, param: string)
cleanDispatcher(): void
}

declare class Output {
colors: any
styles: {
error: any,
success: any,
scenario: any,
basic: any,
debug: any,
log: any,
}

print(msg: string): void
stepShift: number
level(level: number): number
process(process: string): string
debug(msg: string): void
log(msg: string): void
error(msg: string): void
success(msg: string): void
plugin(name: string, msg: string): void
step(step: any): void
suite: {
started: Function
}
test: {
started(test: string): void
passed(test: string): void
failed(test: string): void
skipped(test: string): void
}
scenario: {
started(test: string): void
passed(test: string): void
failed(test: string): void
}
say(message: string, color?: string): void
result(passed: number, failed: number, skipped: number, duration: string): void
}

declare class Config {
create(newConfig: Object): Object
load(configFile: string): Config
get(key: string, val: any): any
append(additionalConfig: Object): Object
reset(): Object
}

declare class Helper {
/** Abstract method to provide required config options */
static _config(): any;
Expand Down Expand Up @@ -129,7 +257,9 @@ declare function BeforeSuite(callback: ICodeceptCallback): void;
declare function After(callback: ICodeceptCallback): void;
declare function AfterSuite(callback: ICodeceptCallback): void;

declare function inject(): { [key: string]: any };
declare function inject(): {
{{injectPageObjects}}
};
declare function locate(selector: LocatorOrString): Locator;
declare function within(selector: LocatorOrString, callback: Function): Promise<any>;
declare function session(selector: LocatorOrString, callback: Function): Promise<any>;
Expand All @@ -140,6 +270,12 @@ declare function secret(secret: any): string;
declare const codeceptjs: any;

declare namespace CodeceptJS {
export const container: Container
export const recorder: Recorder
export const event: CodeceptJSEvent
export const output: Output
export const config: Config

export interface {{I}} {
{{methods}}
}
Expand All @@ -151,6 +287,8 @@ declare module "codeceptjs" {
}
`;

const injectSupportTemplate = ' {{name}}: CodeceptJS.{{name}}';

const pageObjectTemplate = `
export interface {{name}} {
{{methods}}
Expand Down Expand Up @@ -181,20 +319,26 @@ module.exports = function (genPath, options) {

const supports = container.support(); // return all support objects
const exportPageObjects = [];
const injectPageObjects = [];
const callbackParams = [];
// See #1795 and #1799, there's no obvious way to use for-in with Proxies
Reflect.ownKeys(supports).forEach((name) => {
if (name === 'I') return;
callbackParams.push(`${String(name)}:CodeceptJS.${String(name)}`);
if (name === 'I') {
injectPageObjects.push(injectSupportTemplate.replace(/{{name}}/g, String(name)));
return;
}
callbackParams.push(`${String(name)}?:CodeceptJS.${String(name)}`);
const pageObject = supports[name];
const pageMethods = addAllMethodsInObject(pageObject, {}, []);
let pageObjectExport = pageObjectTemplate.replace('{{methods}}', pageMethods.join(''));
pageObjectExport = pageObjectExport.replace('{{name}}', String(name));
injectPageObjects.push(injectSupportTemplate.replace(/{{name}}/g, String(name)));
exportPageObjects.push(pageObjectExport);
});

let definitionsTemplate = template.replace('{{methods}}', methods.join(''));
definitionsTemplate = definitionsTemplate.replace('{{exportPageObjects}}', exportPageObjects.join('\n'));
definitionsTemplate = definitionsTemplate.replace('{{injectPageObjects}}', injectPageObjects.join('\n'));
definitionsTemplate = definitionsTemplate.replace('{{callbackParams}}', `, ${[...callbackParams, '...args: any'].join(', ')}`);
if (translations) {
definitionsTemplate = definitionsTemplate.replace(/\{\{I\}\}/g, translations.I);
Expand Down
3 changes: 2 additions & 1 deletion test/data/sandbox/codecept.inject.po.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
},
"include": {
"I": "./support/custom_steps.js",
"MyPage": "./support/my_page.js"
"MyPage": "./support/my_page.js",
"SecondPage": "./support/second_page.js"
},
"bootstrap": false,
"mocha": {},
Expand Down
5 changes: 5 additions & 0 deletions test/data/sandbox/support/second_page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
secondPageMethod() {
console.log('secondPageMethod');
},
};
59 changes: 59 additions & 0 deletions test/runner/list_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,63 @@ describe('list/def commands', () => {
done();
});
});

it('def should create definition file with internal object', (done) => {
try {
fs.unlinkSync(`${codecept_dir}/steps.d.ts`);
} catch (e) {
// continue regardless of error
}
exec(`${runner} def --config ${codecept_dir}/codecept.inject.po.json`, () => {
const content = fs.readFileSync(`${codecept_dir}/steps.d.ts`).toString();
content.should.include(' export const recorder: Recorder');
content.should.include(' export const event: CodeceptJSEvent');
content.should.include(' export const output: Output');
content.should.include(' export const config: Config');
content.should.include(' export const container: Container');
done();
});
});

it('def should create definition file with inject which contains support objects', (done) => {
try {
fs.unlinkSync(`${codecept_dir}/steps.d.ts`);
} catch (e) {
// continue regardless of error
}
exec(`${runner} def --config ${codecept_dir}/codecept.inject.po.json`, () => {
const content = fs.readFileSync(`${codecept_dir}/steps.d.ts`).toString();
content.should.include('declare function inject(): {');
content.should.include(' MyPage: CodeceptJS.MyPage');
content.should.include(' SecondPage: CodeceptJS.SecondPage');
done();
});
});

it('def should create definition file with inject which contains I object', (done) => {
try {
fs.unlinkSync(`${codecept_dir}/steps.d.ts`);
} catch (e) {
// continue regardless of error
}
exec(`${runner} def --config ${codecept_dir}/codecept.inject.po.json`, () => {
const content = fs.readFileSync(`${codecept_dir}/steps.d.ts`).toString();
content.should.include('declare function inject(): {');
content.should.include(' I: CodeceptJS.I');
done();
});
});

it('def should create definition file with callback params', (done) => {
try {
fs.unlinkSync(`${codecept_dir}/steps.d.ts`);
} catch (e) {
// continue regardless of error
}
exec(`${runner} def --config ${codecept_dir}/codecept.inject.po.json`, () => {
const content = fs.readFileSync(`${codecept_dir}/steps.d.ts`).toString();
content.should.include('type ICodeceptCallback = (i?: CodeceptJS.I, current?:any, MyPage?:CodeceptJS.MyPage, SecondPage?:CodeceptJS.SecondPage, ...args: any) => void;');
done();
});
});
});