From ced35e650c4b4e5514f9428dd6fff46ba85665af Mon Sep 17 00:00:00 2001 From: Sam Maister Date: Tue, 9 Apr 2024 17:26:56 +0100 Subject: [PATCH] test: standardise multiremote and standalone e2es across examples --- example-cjs/e2e-multiremote/api.spec.ts | 22 ++++++++ example-cjs/e2e-standalone/api.spec.ts | 44 ++++++++++++++++ example-cjs/package.json | 4 +- example-cjs/wdio.multiremote.conf.ts | 52 +++++++++++++++++++ .../e2e-multiremote/api.spec.ts | 22 ++++++++ .../e2e-standalone/api.spec.ts | 42 +++++++++++++++ example-electron-builder/package.json | 4 +- .../wdio.multiremote.conf.ts | 26 ++++++++++ example/e2e-standalone/api.spec.ts | 2 +- example/package.json | 3 +- src/cjs/classes.ts | 8 ++- 11 files changed, 223 insertions(+), 6 deletions(-) create mode 100644 example-cjs/e2e-multiremote/api.spec.ts create mode 100644 example-cjs/e2e-standalone/api.spec.ts create mode 100644 example-cjs/wdio.multiremote.conf.ts create mode 100644 example-electron-builder/e2e-multiremote/api.spec.ts create mode 100644 example-electron-builder/e2e-standalone/api.spec.ts create mode 100644 example-electron-builder/wdio.multiremote.conf.ts diff --git a/example-cjs/e2e-multiremote/api.spec.ts b/example-cjs/e2e-multiremote/api.spec.ts new file mode 100644 index 00000000..9572fb71 --- /dev/null +++ b/example-cjs/e2e-multiremote/api.spec.ts @@ -0,0 +1,22 @@ +import { browser } from 'wdio-electron-service'; +import { multiremotebrowser, expect } from '@wdio/globals'; + +const { name, version } = globalThis.packageJson; + +describe('Electron APIs using Multiremote', () => { + it('should retrieve app metadata through the electron API', async () => { + const appName = await browser.electron.execute((electron) => electron.app.getName()); + expect(appName).toStrictEqual([name, name]); + const appVersion = await browser.electron.execute((electron) => electron.app.getVersion()); + expect(appVersion).toStrictEqual([version, version]); + }); + + it('should retrieve instance-specific values from a single instance', async () => { + const browserA = multiremotebrowser.getInstance('browserA'); + expect(await browserA.electron.execute(() => process.argv.includes('--browser=A'))).toBe(true); + expect(await browserA.electron.execute(() => process.argv.includes('--browser=B'))).toBe(false); + const browserB = multiremotebrowser.getInstance('browserB'); + expect(await browserB.electron.execute(() => process.argv.includes('--browser=A'))).toBe(false); + expect(await browserB.electron.execute(() => process.argv.includes('--browser=B'))).toBe(true); + }); +}); diff --git a/example-cjs/e2e-standalone/api.spec.ts b/example-cjs/e2e-standalone/api.spec.ts new file mode 100644 index 00000000..8128f087 --- /dev/null +++ b/example-cjs/e2e-standalone/api.spec.ts @@ -0,0 +1,44 @@ +import path from 'node:path'; +import fs from 'node:fs'; +import process from 'node:process'; + +import { startSession } from 'wdio-electron-service'; +import type { PackageJson } from 'read-package-up'; + +const packageJson = JSON.parse( + fs.readFileSync(path.join(__dirname, '..', 'package.json'), { encoding: 'utf-8' }), +) as PackageJson; + +const getBinaryExtension = () => { + if (process.platform === 'darwin') { + return '.app/Contents/MacOS/wdio-electron-service-example'; + } else if (process.platform === 'win32') { + return '.exe'; + } + + return ''; +}; + +const getBinaryPath = (packageName: string) => + `./out/${packageName}-${process.platform}-${process.arch}/${packageName}${getBinaryExtension()}`; + +async function init() { + const browser = await startSession({ + appBinaryPath: getBinaryPath('wdio-electron-service-example'), + appArgs: ['foo', 'bar=baz'], + }); + + const appName = await browser.electron.execute((electron) => electron.app.getName()); + if (appName !== packageJson.name) { + throw new Error(`appName test failed: ${appName} !== ${packageJson.name}`); + } + + const appVersion = await browser.electron.execute((electron) => electron.app.getVersion()); + if (appVersion !== packageJson.version) { + throw new Error(`appVersion test failed: ${appVersion} !== ${packageJson.version}`); + } + + process.exit(); +} + +init(); diff --git a/example-cjs/package.json b/example-cjs/package.json index ea77b64c..38519b26 100644 --- a/example-cjs/package.json +++ b/example-cjs/package.json @@ -9,7 +9,9 @@ "ci": "pnpm i && pnpm build && pnpm test", "clean": "pnpm clean:dist && rm -rf ./node_modules pnpm-lock.yaml ./wdio-logs ./out", "clean:dist": "pnpx rimraf ./dist && mkdir -p ./dist", - "test": "wdio run ./wdio.conf.ts" + "test": "wdio run ./wdio.conf.ts && pnpm test:multiremote && pnpm test:standalone", + "test:multiremote": "wdio run ./wdio.multiremote.conf.ts", + "test:standalone": "ts-node ./e2e-standalone/api.spec.ts" }, "dependencies": { "wdio-electron-service": "file:../" diff --git a/example-cjs/wdio.multiremote.conf.ts b/example-cjs/wdio.multiremote.conf.ts new file mode 100644 index 00000000..c1663832 --- /dev/null +++ b/example-cjs/wdio.multiremote.conf.ts @@ -0,0 +1,52 @@ +import path from 'node:path'; +import fs from 'node:fs'; +import type { PackageJson } from 'read-package-up'; +import type { Options } from '@wdio/types'; + +const packageJson = JSON.parse( + fs.readFileSync(path.join(__dirname, 'package.json'), { encoding: 'utf-8' }), +) as PackageJson; + +globalThis.packageJson = packageJson; +process.env.TEST = 'true'; + +export const config: Options.Testrunner = { + services: ['electron'], + waitforTimeout: 5000, + connectionRetryCount: 10, + connectionRetryTimeout: 30000, + logLevel: 'debug', + runner: 'local', + autoCompileOpts: { + autoCompile: true, + tsNodeOpts: { + transpileOnly: true, + project: path.join(__dirname, 'tsconfig.json'), + }, + }, + framework: 'mocha', + mochaOpts: { + ui: 'bdd', + timeout: 30000, + }, + outputDir: 'wdio-multiremote-logs', + specs: ['./e2e-multiremote/*.ts'], + capabilities: { + browserA: { + capabilities: { + 'browserName': 'electron', + 'wdio:electronServiceOptions': { + appArgs: ['browser=A'], + }, + } as WebdriverIO.Capabilities, + }, + browserB: { + capabilities: { + 'browserName': 'electron', + 'wdio:electronServiceOptions': { + appArgs: ['browser=B'], + }, + } as WebdriverIO.Capabilities, + }, + }, +}; diff --git a/example-electron-builder/e2e-multiremote/api.spec.ts b/example-electron-builder/e2e-multiremote/api.spec.ts new file mode 100644 index 00000000..9572fb71 --- /dev/null +++ b/example-electron-builder/e2e-multiremote/api.spec.ts @@ -0,0 +1,22 @@ +import { browser } from 'wdio-electron-service'; +import { multiremotebrowser, expect } from '@wdio/globals'; + +const { name, version } = globalThis.packageJson; + +describe('Electron APIs using Multiremote', () => { + it('should retrieve app metadata through the electron API', async () => { + const appName = await browser.electron.execute((electron) => electron.app.getName()); + expect(appName).toStrictEqual([name, name]); + const appVersion = await browser.electron.execute((electron) => electron.app.getVersion()); + expect(appVersion).toStrictEqual([version, version]); + }); + + it('should retrieve instance-specific values from a single instance', async () => { + const browserA = multiremotebrowser.getInstance('browserA'); + expect(await browserA.electron.execute(() => process.argv.includes('--browser=A'))).toBe(true); + expect(await browserA.electron.execute(() => process.argv.includes('--browser=B'))).toBe(false); + const browserB = multiremotebrowser.getInstance('browserB'); + expect(await browserB.electron.execute(() => process.argv.includes('--browser=A'))).toBe(false); + expect(await browserB.electron.execute(() => process.argv.includes('--browser=B'))).toBe(true); + }); +}); diff --git a/example-electron-builder/e2e-standalone/api.spec.ts b/example-electron-builder/e2e-standalone/api.spec.ts new file mode 100644 index 00000000..bfef6dda --- /dev/null +++ b/example-electron-builder/e2e-standalone/api.spec.ts @@ -0,0 +1,42 @@ +import url from 'node:url'; +import path from 'node:path'; +import fs from 'node:fs'; +import process from 'node:process'; + +import { startSession } from 'wdio-electron-service'; +import type { PackageJson } from 'read-package-up'; + +const __dirname = path.dirname(url.fileURLToPath(import.meta.url)); +const packageJson = JSON.parse( + fs.readFileSync(path.join(__dirname, '..', 'package.json'), { encoding: 'utf-8' }), +) as PackageJson; + +const getBinaryExtension = () => { + if (process.platform === 'darwin') { + return '.app/Contents/MacOS/wdio-electron-service-example'; + } else if (process.platform === 'win32') { + return '.exe'; + } + + return ''; +}; + +const getBinaryPath = (packageName: string) => + `./out/${packageName}-${process.platform}-${process.arch}/${packageName}${getBinaryExtension()}`; + +const browser = await startSession({ + appBinaryPath: getBinaryPath('wdio-electron-service-example'), + appArgs: ['foo', 'bar=baz'], +}); + +const appName = await browser.electron.execute((electron) => electron.app.getName()); +if (appName !== packageJson.name) { + throw new Error(`appName test failed: ${appName} !== ${packageJson.name}`); +} + +const appVersion = await browser.electron.execute((electron) => electron.app.getVersion()); +if (appVersion !== packageJson.version) { + throw new Error(`appVersion test failed: ${appVersion} !== ${packageJson.version}`); +} + +process.exit(); diff --git a/example-electron-builder/package.json b/example-electron-builder/package.json index 22f8348d..db30ad9c 100644 --- a/example-electron-builder/package.json +++ b/example-electron-builder/package.json @@ -11,7 +11,9 @@ "ci": "pnpm i && pnpm build && pnpm test", "clean": "pnpm clean:dist && rm -rf ./node_modules pnpm-lock.yaml ./wdio-logs", "clean:dist": "pnpx rimraf ./dist && mkdir -p ./dist", - "test": "wdio run ./wdio.conf.ts" + "test": "wdio run ./wdio.conf.ts && pnpm test:multiremote && pnpm test:standalone", + "test:multiremote": "wdio run ./wdio.multiremote.conf.ts", + "test:standalone": "ts-node ./e2e-standalone/api.spec.ts" }, "devDependencies": { "@rollup/plugin-commonjs": "^25.0.4", diff --git a/example-electron-builder/wdio.multiremote.conf.ts b/example-electron-builder/wdio.multiremote.conf.ts new file mode 100644 index 00000000..41e84397 --- /dev/null +++ b/example-electron-builder/wdio.multiremote.conf.ts @@ -0,0 +1,26 @@ +import type { Options } from '@wdio/types'; +import { config as baseConfig } from './wdio.conf.js'; + +export const config: Options.Testrunner = { + ...baseConfig, + outputDir: 'wdio-multiremote-logs', + specs: ['./e2e-multiremote/*.ts'], + capabilities: { + browserA: { + capabilities: { + 'browserName': 'electron', + 'wdio:electronServiceOptions': { + appArgs: ['browser=A'], + }, + } as WebdriverIO.Capabilities, + }, + browserB: { + capabilities: { + 'browserName': 'electron', + 'wdio:electronServiceOptions': { + appArgs: ['browser=B'], + }, + } as WebdriverIO.Capabilities, + }, + }, +}; diff --git a/example/e2e-standalone/api.spec.ts b/example/e2e-standalone/api.spec.ts index bfef6dda..62e647cc 100644 --- a/example/e2e-standalone/api.spec.ts +++ b/example/e2e-standalone/api.spec.ts @@ -1,5 +1,5 @@ -import url from 'node:url'; import path from 'node:path'; +import url from 'node:url'; import fs from 'node:fs'; import process from 'node:process'; diff --git a/example/package.json b/example/package.json index aea26eec..10e34942 100644 --- a/example/package.json +++ b/example/package.json @@ -11,7 +11,8 @@ "ci": "pnpm i && pnpm build && pnpm test", "clean": "pnpm clean:dist && rm -rf ./node_modules pnpm-lock.yaml ./wdio-logs ./out", "clean:dist": "pnpx rimraf ./dist && mkdir -p ./dist", - "test": "wdio run ./wdio.conf.ts && wdio run ./wdio.multiremote.conf.ts && pnpm test:standalone", + "test": "wdio run ./wdio.conf.ts && pnpm test:multiremote && pnpm test:standalone", + "test:multiremote": "wdio run ./wdio.multiremote.conf.ts", "test:standalone": "ts-node ./e2e-standalone/api.spec.ts", "test:standalone:local": "cd .. && pnpm build && cd - && rm -rf ./node_modules && pnpm i && pnpm test:standalone" }, diff --git a/src/cjs/classes.ts b/src/cjs/classes.ts index 8e899b30..d8f61dad 100644 --- a/src/cjs/classes.ts +++ b/src/cjs/classes.ts @@ -1,12 +1,16 @@ import { Capabilities, Options, Services } from '@wdio/types'; +// Workaround for ts-node converting dynamic imports to requires +// see https://github.com/TypeStrong/ts-node/discussions/1290 +const dynamicImport = new Function('specifier', 'return import(specifier)'); + export class CJSElectronLauncher { private instance?: Promise; constructor(options: unknown, caps: unknown, config: Options.Testrunner) { this.instance = (async () => { const importPath = '../service.js'; - const { default: ElectronService } = await import(importPath); + const { default: ElectronService } = await dynamicImport(importPath); return new ElectronService(options, caps, config); })(); } @@ -23,7 +27,7 @@ export class CJSElectronService { constructor(globalOptions: unknown) { this.instance = (async () => { const importPath = '../service.js'; - const { default: ElectronService } = await import(importPath); + const { default: ElectronService } = await dynamicImport(importPath); return new ElectronService(globalOptions); })(); }