diff --git a/e2e.config.js b/e2e.config.js new file mode 100644 index 0000000..373b3ca --- /dev/null +++ b/e2e.config.js @@ -0,0 +1,17 @@ +module.exports = { + selectorLabel: 'class', + params: { + projects: { + '${projectName}': { + type: 'uri', + location: 'https://cn.bing.com/', + } + } + }, + lookupConfig({ + config, + tag + }) { + return config.params.projects[tag.project]; + }, +}; diff --git a/packages/tees/__test__/src/lifecycle/postSetup.test.js b/packages/tees/__test__/src/lifecycle/postSetup.test.js new file mode 100644 index 0000000..dd5dda4 --- /dev/null +++ b/packages/tees/__test__/src/lifecycle/postSetup.test.js @@ -0,0 +1,309 @@ +const { + caseParams, + caseParamsSkipped, + query, + instance, + caseTitle, + contextParamExpected, + context, + argFn, + execCaseParamExpected +} = require('./postSetupTestData.js'); +const { setup, testPrepare } = require('../../../src/lifecycle/setup.js'); +const { + beforeEachStart, + afterEachEnd, + getExecCaseParams, + execCase, + testCase, + testOnly, +} = require('../../../src/lifecycle/postSetup'); + + +describe('postSetup unit test : beforeEachStart', () => { + + it('beforeEachStart, before hook for each case should be called', async () => { + const mockFn = jest.fn(); + + const result = await getExecCaseParams(context); + await beforeEachStart(result.context, mockFn); + + expect(mockFn).toBeCalled(); + expect(JSON.stringify(mockFn.mock.calls[0][0])).toBe(JSON.stringify(contextParamExpected)); + + }); +}), + + describe('postSetup unit test : afterEachEnd', () => { + const hook1 = jest.fn(); + const hook2 = jest.fn(); + const close = jest.fn(); + const afterEachParam = { + driver: { + afterHooks: [ + hook1, + hook2 + ], + close + }, + options: { + isSandbox: true + }, + } + const afterEachFn = jest.fn(); + + it('afterEachEnd, after hook for each case should be called and afterEachParam.driver.afterHooks should be called', async () => { + + await afterEachEnd(afterEachParam, afterEachFn); + + expect(afterEachFn).toBeCalled(); + expect(hook1).toBeCalled(); + expect(hook2).toBeCalled(); + }); + + it('afterEachEnd, after hook for each case should be called and afterEachParam.driver.afterHooks should be called', async () => { + afterEachParam.options.isSandbox = true; + + await afterEachEnd(afterEachParam, afterEachFn); + expect(close).toBeCalled(); + }); + + it('afterEachEnd, when afterHooks arg is null', async () => { + afterEachParam.driver.afterHooks = null; + + await afterEachEnd(afterEachParam, afterEachFn).catch(e => expect(String(e)).toMatch('TypeError: driver.afterHooks is not iterable')); + + }); + + }), + + describe('postSetup unit test : getExecCaseParams', () => { + + it(`getExecCaseParams, should return result`, async () => { + + const result = await getExecCaseParams(context); + + expect(result.caseTitle).toEqual(caseTitle); + expect(JSON.stringify(result.instance.query)).toEqual(JSON.stringify(query)); + expect(JSON.stringify(result.context)).toEqual(JSON.stringify(contextParamExpected)); + }); + + it(`getExecCaseParams, when case tags don't include loginAccount`, async () => { + context.option = { loginAccount: 'loginAccount' }; + const caseTitleNoLoginAccount = `execute case => (google in levels-p3 & brands-rc & tags-salesforce & options-accounts & loginAccount-loginAccount on puppeteer)`; + contextParamExpected.options.option = context.option; + + const result = await getExecCaseParams(context); + + expect(result.caseTitle).toEqual(caseTitleNoLoginAccount); + expect(result.context.options.option).toEqual(context.option); + }); + + it(`getExecCaseParams, when case tags don't include account`, async () => { + context.option = { accounts: ['account'] }; + const caseTitleNoAccounts = `execute case => (google in levels-p3 & brands-rc & tags-salesforce & options-accounts & accounts-account on puppeteer)`; + + const result = await getExecCaseParams(context); + + expect(result.caseTitle).toEqual(caseTitleNoAccounts); + expect(result.context.options.option).toEqual(context.option); + }); + }), + + describe('postSetup unit test : execCase', () => { + + it('execCase, verify the params of global and global.beforeEachStart should be called', async () => { + + let result = await getExecCaseParams(context); + + global.getExecCaseParams = jest.fn().mockReturnValue(result); + + global.beforeEachStart = jest.fn(); + + result.context.driver.run = jest.fn(); + result.context.driver.newPage = jest.fn(); + result.context.driver.goto = jest.fn(); + + await execCase(context); + + expect(JSON.stringify(global.$)).toEqual(JSON.stringify(result.instance.query)); + expect(global.__context__).toEqual(result.context); + expect(global.__beforeEachCase__).toEqual(result.beforeEachCase); + expect(global.__afterEachCase__).toEqual(result.afterEachCase); + + expect(global.beforeEachStart).toBeCalled(); + expect(global.beforeEachStart).toHaveBeenCalledWith(result.context, result.beforeEachCase); + + expect(result.context.driver.run).toBeCalled(); + + }); + + it('execCase, if test only should assign the params of global', async () => { + + context.isOnly = true; + + result = await getExecCaseParams(context); + global.getExecCaseParams = jest.fn().mockReturnValue(result); + + global.beforeEachStart = jest.fn(); + + result.context.driver.run = jest.fn(); + result.context.driver.newPage = jest.fn(); + result.context.driver.goto = jest.fn(); + + await execCase(context); + + expect(result.context.driver.run).toBeCalled(); + }); + + it('execCase, if isUT is true context.driver.run should not be called', async () => { + + context.driver = "ut"; + + result = await getExecCaseParams(context); + + global.getExecCaseParams = jest.fn().mockReturnValue(result); + + global.beforeEachStart = jest.fn(); + + result.context.driver.run = jest.fn(); + result.context.driver.newPage = jest.fn(); + result.context.driver.goto = jest.fn(); + + await execCase(context); + + expect(result.context.driver.run).not.toBeCalled(); + + context.driver = "puppeteer"; + + }); + + it('execCase, verify the params of global and global.beforeEachStart should be called', async () => { + + context.isHeadless = true; + context.isDebugger = true; + context.isVerbose = true; + + result = await getExecCaseParams(context); + + global.getExecCaseParams = jest.fn().mockReturnValue(result); + + global.beforeEachStart = jest.fn(); + + result.context.driver.run = jest.fn(); + result.context.driver.newPage = jest.fn(); + result.context.driver.goto = jest.fn(); + + await execCase(context); + + expect(result.context.driver.run).toBeCalled(); + + }); + + + it('execCase, if isSandbox is false context.driver.run should not be called', async () => { + + context.isSandbox = false; + + const result = await getExecCaseParams(context); + + global.getExecCaseParams = jest.fn().mockReturnValue(result); + + global.beforeEachStart = jest.fn(); + + result.context.driver.run = jest.fn(); + result.context.driver.newPage = jest.fn(); + result.context.driver.goto = jest.fn(); + + await execCase(context); + + expect(result.context.driver.run).not.toBeCalled(); + + }); + + }), + + describe('postSetup unit test : testCase', () => { + + it(`testCase, global.execCase should be called and the param is equal to ${execCaseParamExpected}`, async () => { + + global.execCase = jest.fn(); + await testCase(caseParams, argFn); + + expect(global.execCase).toBeCalled(); + expect(global.execCase).toHaveBeenCalledWith(execCaseParamExpected); + expect(global.execCase.mock.instances.length).toBe(6); + }); + + it(`testCase, the param isOnly is true and global.execCase should be called `, async () => { + + global.execCase = jest.fn(); + await testCase(caseParams, argFn, true); + + expect(global.execCase).toBeCalled(); + expect(global.execCase.mock.calls[0][0].isOnly).toEqual(true); + + }); + + it(`testCase, when modes is not null and global.execCase should be called`, async () => { + + global.execCase = jest.fn(); + caseParams.modes = ['headless']; + + await testCase(caseParams, argFn); + + expect(global.execCase).toBeCalled(); + expect(global.execCase.mock.calls[0][0].modes).toEqual([...caseParams.modes, ...global.execModes]); + + }); + + it(`testCase, when tags is [] and global.execCase should be called`, async () => { + + global.execCase = jest.fn(); + caseParams.tags = []; + + await testCase(caseParams, argFn); + + expect(global.execCase).not.toBeCalled(); + }); + + it(`testCase, when options is [{}] and global.execCase should be called`, async () => { + + global.execCase = jest.fn(); + caseParams.options = [{}]; + + await testCase(caseParams, argFn); + + expect(global.execCase).not.toBeCalled(); + }); + + it(`testCase, isSkiped is true and global.execCase should not be called`, async () => { + + global.execCase = jest.fn(); + await testCase(caseParamsSkipped, argFn); + + expect(global.execCase).not.toBeCalled(); + + }); + + }), + + describe('postSetup unit test : testOnly', () => { + + it(`testOnly, return global.testCase `, async () => { + + const fn = jest.fn(); + global.test = jest.fn(); + + const result = await testOnly(caseParams, fn); + + expect(global.test).toBeCalled(); + expect(global.test.mock.calls[0][0]).toBe(caseParams); + expect(global.test.mock.calls[0][1]).toBe(fn); + expect(global.test.mock.calls[0][2]).toBe(true); + expect(result).toBe(global.test(caseParams, fn, true)); + + }); + + + }); diff --git a/packages/tees/__test__/src/lifecycle/postSetupTestData.js b/packages/tees/__test__/src/lifecycle/postSetupTestData.js new file mode 100644 index 0000000..85b2db2 --- /dev/null +++ b/packages/tees/__test__/src/lifecycle/postSetupTestData.js @@ -0,0 +1,272 @@ +//global params +global.afterEach = jest.fn() + +const accounts = { + googleAccount: 'username', + googlePwd: 'password', +}; + +const drivers = ['enzyme', 'puppeteer', 'firefox', 'safari', 'chrome', 'webextGeckodriver']; + +const params = { + projects: { + google: { + type: 'extension', + source: 'source', + driver: { + setting: { + defaultViewport: { + height: 650, + width: 1100, + }, + args: [ + 'args', + ] + } + }, + params: { + brands: { + rc: { + ...accounts, + extension: 'extension', + location: 'location', + }, + } + } + }, + }, + drivers, + levels: ['p0', 'p1', 'p2', 'p3'], + brands: ['rc', 'bt', 'telus', 'att'], + tags: [['google'], ['office'], ['salesforce']], + options: ['accounts'], +} + +const defaults = { + drivers, + levels: ['p3'], + brands: ['rc'], + accounts: ['account'], + tags: [['google'], ['office'], ['salesforce']], + options: ['accounts'], +} + +global.execGlobal = { + defaults, + params, + selectorLabel: 'selectorLabel', +} + +global.execTags = [['google', + { + drivers, + levels: ['p3'], + brands: ['rc'], + tags: ['salesforce'], + options: ['accounts'], + accounts: ['account'], + }]] +global.execDrivers = defaults.drivers; +global.drivers = defaults.drivers; +global.execModes = [] +global.afterEach = jest.fn(); +global.beforeEachCase = jest.fn(); +global.afterEachCase = jest.fn(); +global.test = jest.fn((caseTitle, func) => func({ + instance: { + query + }, + context: contextParamExpected, + beforeEachCase: global.beforeEachCase, + afterEachCase: global.afterEachCase +})) + +global.test.only = jest.fn((caseTitle, func) => func({ + instance: { + query + }, + context: contextParamExpected, + beforeEachCase: global.beforeEachCase, + afterEachCase: global.afterEachCase +})) + +const caseParams = { + title: 'test postSetup', + tags: [['google'], ['office'], ['salesforce']], + options: ['accounts'], + modes: [] +} + +const caseParamsSkipped = { + title: 'test postSetup', + tags: [['google'], ['office'], ['salesforce']], + options: [{ + accounts: ['account'], + loginAccount: 'loginAccount', + callingType: 'Other Phone', + }], +} + +const query = node => new Query(node, { + label: inputSetting.selectorLabel +}) + +const helper = require('../../../src/lifecycle/helper'); + +const caseTitle = "execute case => (google in levels-p3 & brands-rc & tags-salesforce & options-accounts & accounts-account & loginAccount-loginAccount on puppeteer)" + +const mockProcessArgv = () => process.argv = ['/usr/local/bin/node', + '../../../node_modules/jest/bin/jest.js', + '--config={"verbose":true,"testMatch":["/packages/tees/templates/exampleSpec.js"],"testPathIgnorePatterns":[],"setupFiles":["/packages/tees/src/lifecycle/setup.js"],"setupFilesAfterEnv":["/packages/tees/src/lifecycle/postSetup.js"],"globals":{"hasReporter":false,"configPath":"../../../../tees/e2e.config.js","retryTimes":0,"execTags":[],"execModes":[],"execDrivers":["puppeteer"],"execGlobal":{"selectorLabel":"class","params":{"drivers":["puppeteer"],"projects":{"${projectName}":{"type":"uri","location":"https://cn.bing.com/"}}},"exec":{"drivers":["puppeteer"]},"defaults":{"drivers":["puppeteer"]}},"execDefaults":{"browsers":{}}},"globalSetup":"tees-environment/setup","globalTeardown":"tees-environment/teardown","testEnvironment":"tees-environment","transform":{"^.+\\\\.(jsx|js)$":"babel-jest"},"testRunner":"jest-circus/runner"}', + '--forceExit', + '--no-cache', + '--detectOpenHandles']; + +mockProcessArgv(); + +const instance = helper.getDriverInstance({ drivers: global.drivers, driver: "puppeteer", isSandbox: true }); + +let driversObj = {}; +global.drivers.forEach(element => { + driversObj[element] = instance; +}); + +global.drivers = driversObj; + +const argFn = jest.fn(); +let context = { + driver: "puppeteer", + option: { + accounts: ['account'], + loginAccount: 'loginAccount', + }, + title: 'execute case', + project: "google", + group: ['levels-p3', + 'brands-rc', + 'tags-salesforce', + 'options-accounts' + ], + caseParams, + tag: { project: 'google' }, + modes: [], + caseTag: ['google', + { + drivers, + levels: ['p3'], + brands: ['rc'], + tags: [['google'], ['office'], ['salesforce']], + options: ['accounts'], + accounts: ['account'] + } + ], + isSandbox: true, + isHeadless: false, + isDebugger: false, + isVerbose: false, + isOnly: false, + fn: argFn +} + +let contextParamExpected = { + logger: helper.generateLogger(caseTitle, global.hasReporter), + get browser() { + return instance.driver.browser; + }, + get page() { + return instance.driver.page; + }, + driver: instance.driver, + options: { + option: context.option, + config: { + type: "extension", + source: "source", + driver: { + setting: { + defaultViewport: { + height: 650, + width: 1100 + }, + args: [ + "args" + ] + } + }, + params: { + brands: { + rc: { + googleAccount: "username", + googlePwd: "password", + extension: "extension", + location: "location" + } + } + } + }, + tag: { + project: "google" + }, + driver: "puppeteer", + modes: [], + isSandbox: true, + isHeadless: false, + isDebugger: false, + isVerbose: false, + isVirtual: false, + isUT: false + } +} + + + +let execCaseParamExpected = { + driver: "enzyme", + option: "accounts", + title: 'test postSetup', + project: "google", + group: ['levels-p3', + 'brands-rc', + 'tags-salesforce', + 'options-accounts', + 'accounts-account' + ], + caseParams, + tag: { + "accounts": "account", + "brands": "rc", + "levels": "p3", + "options": "accounts", + "project": "google", + "tags": "salesforce", + }, + modes: [], + caseTag: ['google', + { + drivers, + levels: ['p3'], + brands: ['rc'], + tags: [['google'], ['office'], ['salesforce']], + options: ['accounts'], + accounts: ['account'] + } + ], + isSandbox: false, + isHeadless: false, + isDebugger: false, + isVerbose: false, + isOnly: false, + fn: argFn +} + +module.exports = { + caseParams, + caseParamsSkipped, + instance, + query, + caseTitle, + contextParamExpected, + context, + argFn, + execCaseParamExpected +}; diff --git a/packages/tees/src/lifecycle/postSetup.js b/packages/tees/src/lifecycle/postSetup.js index a713fa0..874548e 100644 --- a/packages/tees/src/lifecycle/postSetup.js +++ b/packages/tees/src/lifecycle/postSetup.js @@ -57,10 +57,10 @@ async function afterEachEnd(context, afterHook) { } /** - * execute case with case config and exection config. + * exection config * @param {object} params - execution params. */ -function execCase({ +function getExecCaseParams({ driver, option, title, @@ -89,18 +89,18 @@ function execCase({ option, caseTag, tag - }); + }) const groupInfos = group.length > 0 ? `in ${group.join(' & ')} ` : ''; const _optionTags = Object.entries(_option) - .reduce((tags, [name, value]) => { - const isAccountTag = ['loginAccount', 'accounts'].includes(name); - if (!isAccountTag) return tags; - return [ - ...tags, - `& ${name}-${value}` - ]; - }, []).join(' '); - const tail = ` => (${project} ${groupInfos}${_optionTags}on ${driver})`; + .reduce((tags, [name, value]) => { + const isAccountTag = ['loginAccount', 'accounts'].includes(name); + if (!isAccountTag) return tags; + return [ + ...tags, + `& ${name}-${value}` + ]; + }, []).join(' '); + const tail = ` => (${project} ${groupInfos}${_optionTags} on ${driver})`; const caseTitle = `${name}${tail}`; const { config, @@ -116,8 +116,9 @@ function execCase({ modes, isSandbox, }); + const context = { - logger: generateLogger(caseTitle, global.hasReporter, isVerbose), + logger: generateLogger(caseTitle, global.hasReporter), driver: instance.driver, get browser() { return context.driver.browser; @@ -141,7 +142,64 @@ function execCase({ }; // cache serialization options for retry. __optionsMapping__.set(context, JSON.stringify(context.options)); + + return { + caseTitle, + instance, + context, + beforeEachCase, + afterEachCase, + } + +} + +/** + * execute case with case config and exection config. + * @param {object} params - execution params. + */ +function execCase({ + driver, + option, + title, + project, + group, + caseParams, + tag, + modes, + caseTag, + isSandbox, + isHeadless, + isDebugger, + isVerbose, + isOnly, + fn, +}) { /* eslint-disable */ + + const { + caseTitle, + instance, + context, + beforeEachCase, + afterEachCase, + } = global.getExecCaseParams({ + driver, + option, + title, + project, + group, + caseParams, + tag, + modes, + caseTag, + isSandbox, + isHeadless, + isDebugger, + isVerbose, + isOnly, + fn, + }) + const func = (async function ({ instance, context, @@ -152,7 +210,8 @@ function execCase({ global.__context__ = context; global.__beforeEachCase__ = beforeEachCase; global.__afterEachCase__ = afterEachCase; - await beforeEachStart(context, beforeEachCase); + + await global.beforeEachStart(context, beforeEachCase); if (!context.options.isUT) { if (context.options.isSandbox) { const isAuth = context.options.option.isAuth; @@ -218,7 +277,7 @@ function testCase(caseParams, fn, isOnly = false) { if (isSkipped) { break; } - execCase({ + global.execCase({ driver, option, title, @@ -253,7 +312,7 @@ function testSkip(...args) { } function testOnly(...args) { - return testCase(...args, true); + return global.test(...args, true); } function testDescribe(...args) { @@ -267,4 +326,18 @@ global.test = testCase; global.describe = testDescribe; global.describe.skip = _describe.skip; global.test.skip = testSkip; -global.test.only = testOnly; \ No newline at end of file +global.test.only = testOnly; +global.execCase = execCase; +global.beforeEachStart = beforeEachStart; +global.getExecCaseParams = getExecCaseParams; + +module.exports = { + beforeEachStart, + afterEachEnd, + getExecCaseParams, + execCase, + testCase, + testSkip, + testOnly, + testDescribe +}; \ No newline at end of file