diff --git a/README.md b/README.md index e79bcd18..ab79a3b8 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,32 @@ module.exports = { } ``` +From version **0.0.13** you can run you tests for multiple devices. + +```javascript +module.exports = { + devices: ["iPhone 6", "Pixel 2"], + ... +} +``` + +It will run your tests depending on you playwright package. + +- If you are using specific playwright package, it will run test for this specific browser +- With installed **playwright** package you can define browsers with config: + +```javascript +module.exports = { + browsers: ["chromium", "firefox"], + devices: ["iPhone 6", "Pixel 2"], + ... +} +``` + +If there is no defined browsers in config it will run tests for chromium browser. + +[More details](https://github.com/mmarkelov/jest-playwright/pull/54#issuecomment-592514337) + - You must run your tests with **jest-playwright** ```json diff --git a/src/bin/testProcess.ts b/src/bin/testProcess.ts index f72dd1f9..33c55363 100644 --- a/src/bin/testProcess.ts +++ b/src/bin/testProcess.ts @@ -1,50 +1,97 @@ import { spawn, spawnSync, SpawnSyncOptions } from 'child_process' -import { readConfig } from '../utils' -import { checkBrowsers, getResultByStatus } from './utils' -import { PARALLEL, BrowserType } from '../constants' +import { + checkBrowserEnv, + getBrowserType, + readConfig, + readPackage, +} from '../utils' +import { checkCommand, getExitCode, getLogMessage } from './utils' +import { BrowserType, CORE, PARALLEL, PLAYWRIGHT } from '../constants' -const getSpawnOptions = (browser: BrowserType): SpawnSyncOptions => ({ +const getSpawnOptions = ( + browser: BrowserType, + device: string | null, +): SpawnSyncOptions => ({ stdio: 'inherit', shell: true, env: { ...process.env, BROWSER: browser, + ...(device ? { DEVICE: device } : {}), }, }) const exec = ({ sequence, browser, + device = null, params, }: { sequence: string browser: BrowserType + device?: string | null params: string[] -}): void => { - const options = getSpawnOptions(browser) - if (sequence === PARALLEL) { - const process = spawn( - 'node', - [`node_modules/jest/bin/jest.js ${params}`], - options, - ) - process.on('close', status => { - console.log(`${getResultByStatus(status)} tests for ${browser}\n\n`) - }) - } else { - const { status } = spawnSync( - 'node', - [`node_modules/jest/bin/jest.js ${params}`], - options, - ) - console.log(`${getResultByStatus(status)} tests for ${browser}`) - } -} +}): Promise => + new Promise(resolve => { + const options = getSpawnOptions(browser, device) + if (sequence === PARALLEL) { + const process = spawn( + 'node', + [`node_modules/jest/bin/jest.js ${params}`], + options, + ) + process.on('close', status => { + console.log(getLogMessage(browser, status, device)) + resolve(status) + }) + } else { + const { status } = spawnSync( + 'node', + [`node_modules/jest/bin/jest.js ${params}`], + options, + ) + console.log(getLogMessage(browser, status, device)) + resolve(status) + } + }) const runner = async (sequence: string, params: string[]): Promise => { - const { browsers = [] } = await readConfig() - checkBrowsers(browsers) - browsers.forEach(browser => exec({ sequence, browser, params })) + const { browsers = [], devices = [] } = await readConfig() + let exitCodes: (number | null)[] = [] + checkCommand(browsers, devices) + if (!browsers.length && devices.length) { + let browserType: BrowserType + const browser = await readPackage() + if (browser === PLAYWRIGHT || browser === CORE) { + const config = await readConfig() + browserType = getBrowserType(config) + checkBrowserEnv(browserType) + } else { + browserType = browser + } + exitCodes = await Promise.all( + devices.map(device => + exec({ sequence, browser: browserType, device, params }), + ), + ) + } + if (browsers.length) { + if (devices.length) { + const multipleCodes = await Promise.all( + browsers.map(browser => + Promise.all( + devices.map(device => exec({ sequence, browser, device, params })), + ), + ), + ) + exitCodes = multipleCodes.reduce((acc, val) => acc.concat(val), []) + } else { + exitCodes = await Promise.all( + browsers.map(browser => exec({ sequence, browser, params })), + ) + } + } + getExitCode(exitCodes) } export default runner diff --git a/src/bin/utils.test.ts b/src/bin/utils.test.ts index cc4a957e..ae53f9b9 100644 --- a/src/bin/utils.test.ts +++ b/src/bin/utils.test.ts @@ -1,21 +1,20 @@ -import { checkBrowsers, getResultByStatus } from './utils' +import { + checkCommand, + getResultByStatus, + getLogMessage, + getExitCode, +} from './utils' import { BrowserType } from '../constants' -describe('checkBrowsers', () => { - it('should throw an error without arguments', () => { - expect(() => checkBrowsers()).toThrow( - 'You should define browsers with your jest-playwright.config.js', - ) - }) - it('should throw an error when passed empty array', () => { - expect(() => checkBrowsers([])).toThrow( - 'You should define browsers with your jest-playwright.config.js', +describe('checkCommand', () => { + it('should throw an error with empty browsers and devices', () => { + expect(() => checkCommand([], [])).toThrow( + 'You should define browsers or devices with your jest-playwright.config.js', ) }) + it('should throw an error when passed wrong browser', () => { - expect(() => - checkBrowsers(['chromium', 'unknown' as BrowserType]), - ).toThrow() + expect(() => checkCommand(['unknown' as BrowserType], [])).toThrow() }) }) @@ -32,3 +31,33 @@ describe('getResultByStatus', () => { expect(getResultByStatus(0)).toBe('Passed') }) }) + +describe('getLogMessage', () => { + it('should return right log', () => { + expect(getLogMessage('chromium', 0, null)).toBe( + 'Passed tests for browser: chromium \n\n', + ) + }) + + it('should return right log', () => { + expect(getLogMessage('chromium', 1, 'iPhone 6')).toBe( + 'Failed tests for browser: chromium and device: iPhone 6\n\n', + ) + }) +}) + +describe('getExitCode', () => { + const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => { + return undefined as never + }) + + it('should exit with code 1 for some failed tests', () => { + getExitCode([0, 0, 1, null]) + expect(mockExit).toHaveBeenCalledWith(1) + }) + + it('should exit with code 0 for passed tests', () => { + getExitCode([0, 0, 0, 0]) + expect(mockExit).toHaveBeenCalledWith(0) + }) +}) diff --git a/src/bin/utils.ts b/src/bin/utils.ts index 50aa3620..74f5a5e1 100644 --- a/src/bin/utils.ts +++ b/src/bin/utils.ts @@ -1,15 +1,39 @@ import { checkBrowserEnv } from '../utils' import { BrowserType } from '../constants' -export const checkBrowsers = (browsers?: BrowserType[]): void => { - if (!browsers || !browsers.length) { +export const checkCommand = ( + browsers: BrowserType[], + devices: string[], +): void => { + if (!browsers.length && !devices.length) { throw new Error( - 'You should define browsers with your jest-playwright.config.js', + 'You should define browsers or devices with your jest-playwright.config.js', ) } browsers.forEach(checkBrowserEnv) + // TODO Add check for devices + // devices.forEach(checkDeviceEnv) } export const getResultByStatus = (status: number | null): string => { return status !== 0 ? 'Failed' : 'Passed' } + +export const getLogMessage = ( + browser: BrowserType, + status: number | null, + device: string | null, +): string => { + return `${getResultByStatus(status)} tests for browser: ${browser} ${ + device ? `and device: ${device}` : '' + }\n\n` +} + +export const getExitCode = (exitCodes: (number | null)[]): void => { + if (exitCodes.every(code => code === 0)) { + process.exit(0) + } else { + console.log('One of the test has not passed successfully') + process.exit(1) + } +} diff --git a/src/constants.ts b/src/constants.ts index 700c5d89..a29196ac 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -2,11 +2,19 @@ import { LaunchOptions } from 'playwright-core/lib/server/browserType' import { BrowserContextOptions } from 'playwright-core/lib/browserContext' import { JestDevServerOptions } from 'jest-dev-server' -export type BrowserType = 'chromium' | 'firefox' | 'webkit' +export const CORE = 'core' +export const PLAYWRIGHT = 'playwright' -export const CHROMIUM: BrowserType = 'chromium' -export const FIREFOX: BrowserType = 'firefox' -export const WEBKIT: BrowserType = 'webkit' +export const CHROMIUM = 'chromium' +export const FIREFOX = 'firefox' +export const WEBKIT = 'webkit' + +export type BrowserType = typeof CHROMIUM | typeof FIREFOX | typeof WEBKIT + +export type PlaywrightRequireType = + | BrowserType + | typeof PLAYWRIGHT + | typeof CORE export const PARALLEL = '--parallel' @@ -17,6 +25,7 @@ export interface Config { browser?: BrowserType browsers?: BrowserType[] device?: string + devices?: string[] server?: JestDevServerOptions } diff --git a/src/utils.ts b/src/utils.ts index 1d0b0aa5..8909c406 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -2,25 +2,26 @@ import fs from 'fs' import path from 'path' import { promisify } from 'util' import { + BrowserType, CHROMIUM, + Config, + CORE, + DEFAULT_CONFIG, FIREFOX, + PLAYWRIGHT, + PlaywrightRequireType, WEBKIT, - DEFAULT_CONFIG, - Config, - BrowserType, } from './constants' import { BrowserType as PlayWrightBrowserType } from 'playwright' const exists = promisify(fs.exists) -type PlaywrightRequireType = BrowserType | 'core' | 'playwright' - const checkDependencies = ( dependencies: Record, ): PlaywrightRequireType | null => { if (!dependencies) return null - if (dependencies.playwright) return 'playwright' - if (dependencies['playwright-core']) return 'core' + if (dependencies.playwright) return PLAYWRIGHT + if (dependencies[`playwright-${CORE}`]) return CORE if (dependencies[`playwright-${CHROMIUM}`]) return CHROMIUM if (dependencies[`playwright-${FIREFOX}`]) return FIREFOX if (dependencies[`playwright-${WEBKIT}`]) return WEBKIT @@ -68,7 +69,7 @@ export const readPackage = async (): Promise => { const packageConfig = await require(absConfigPath) // for handling the local tests if (packageConfig.name === 'jest-playwright-preset') { - return 'core' + return CORE } const playwright = checkDependencies(packageConfig.dependencies) || @@ -83,11 +84,11 @@ export const getPlaywrightInstance = async ( browserType: BrowserType, ): Promise => { const playwrightPackage = await readPackage() - if (playwrightPackage === 'playwright') { + if (playwrightPackage === PLAYWRIGHT) { return require('playwright')[browserType] } - if (playwrightPackage === 'core') { - const browser = require('playwright-core')[browserType] + if (playwrightPackage === CORE) { + const browser = require(`playwright-${CORE}`)[browserType] await browser.downloadBrowserIfNeeded() return browser }