Skip to content

Commit

Permalink
Support different jest environments (#116)
Browse files Browse the repository at this point in the history
* Support different jest environments

* Fix utils test

* Clear some unnecessary close functions
  • Loading branch information
mmarkelov authored May 14, 2020
1 parent a55e114 commit d41ca51
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 184 deletions.
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
module.exports = require('./lib/PlaywrightEnvironment').default
module.exports.getPlaywrightEnv = require('./lib/PlaywrightEnvironment').getPlaywrightEnv
module.exports.globalSetup = require('./lib/global').setup
module.exports.globalTeardown = require('./lib/global').teardown
279 changes: 138 additions & 141 deletions src/PlaywrightEnvironment.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,18 @@
/* eslint-disable no-console */
/* eslint-disable @typescript-eslint/ban-ts-ignore */
import NodeEnvironment from 'jest-environment-node'
import { Config as JestConfig } from '@jest/types'
import { Event, State } from 'jest-circus'
import type { Config as JestConfig } from '@jest/types'
import type { Event, State } from 'jest-circus'
import type { Browser } from 'playwright-core'
import playwright from 'playwright-core'
import type { Config, GenericBrowser, BrowserType } from './types'
import { CHROMIUM, IMPORT_KIND_PLAYWRIGHT } from './constants'
import {
getBrowserType,
getDeviceType,
getPlaywrightInstance,
readConfig,
readPackage,
} from './utils'
import {
Config,
CHROMIUM,
GenericBrowser,
IMPORT_KIND_PLAYWRIGHT,
BrowserType,
} from './constants'
import playwright, { Browser } from 'playwright-core'

const handleError = (error: Error): void => {
process.emit('uncaughtException', error)
Expand Down Expand Up @@ -64,154 +59,156 @@ const getBrowserPerProcess = async (
}
}

class PlaywrightEnvironment extends NodeEnvironment {
private _config: JestConfig.ProjectConfig
constructor(config: JestConfig.ProjectConfig) {
super(config)
this._config = config
}
export const getPlaywrightEnv = (basicEnv = 'node') => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const RootEnv = require(basicEnv === 'node'
? 'jest-environment-node'
: 'jest-environment-jsdom')

async setup(): Promise<void> {
const config = await readConfig(this._config.rootDir)
//@ts-ignore
const browserType = getBrowserType(this._config.browserName)
const { context, exitOnPageError, server, selectors } = config
const playwrightPackage = await readPackage()
if (playwrightPackage === IMPORT_KIND_PLAYWRIGHT) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const playwright = require('playwright')
if (selectors) {
await Promise.all(
selectors.map(({ name, script }) => {
return playwright.selectors.register(name, script)
}),
)
}
return class PlaywrightEnvironment extends RootEnv {
private _config: JestConfig.ProjectConfig

constructor(config: JestConfig.ProjectConfig) {
super(config)
this._config = config
}
//@ts-ignore
const device = getDeviceType(this._config.device)
const playwrightInstance = await getPlaywrightInstance(
playwrightPackage,
browserType,
)
let contextOptions = context

if (server) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const devServer = require('jest-dev-server')
const { setup, ERROR_TIMEOUT, ERROR_NO_COMMAND } = devServer
teardownServer = devServer.teardown
try {
await setup(server)
} catch (error) {
if (error.code === ERROR_TIMEOUT) {
logMessage({
message: error.message,
action: 'can set "server.launchTimeout"',
})

async setup(): Promise<void> {
const config = await readConfig(this._config.rootDir)
//@ts-ignore
const browserType = getBrowserType(this._config.browserName)
const { context, exitOnPageError, server, selectors } = config
const playwrightPackage = await readPackage()
if (playwrightPackage === IMPORT_KIND_PLAYWRIGHT) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const playwright = require('playwright')
if (selectors) {
await Promise.all(
selectors.map(({ name, script }) => {
return playwright.selectors.register(name, script)
}),
)
}
if (error.code === ERROR_NO_COMMAND) {
logMessage({
message: error.message,
action: 'must set "server.command"',
})
}
//@ts-ignore
const device = getDeviceType(this._config.device)
const playwrightInstance = await getPlaywrightInstance(
playwrightPackage,
browserType,
)
let contextOptions = context

if (server) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const devServer = require('jest-dev-server')
const { setup, ERROR_TIMEOUT, ERROR_NO_COMMAND } = devServer
teardownServer = devServer.teardown
try {
await setup(server)
} catch (error) {
if (error.code === ERROR_TIMEOUT) {
logMessage({
message: error.message,
action: 'can set "server.launchTimeout"',
})
}
if (error.code === ERROR_NO_COMMAND) {
logMessage({
message: error.message,
action: 'must set "server.command"',
})
}
throw error
}
throw error
}
}

if (device) {
const { viewport, userAgent } = playwright.devices[device]
contextOptions = { viewport, userAgent, ...contextOptions }
}
this.global.browserName = browserType
this.global.deviceName = device
this.global.browser = await getBrowserPerProcess(
playwrightInstance,
browserType,
config,
)
this.global.context = await this.global.browser.newContext(contextOptions)
this.global.page = await this.global.context.newPage()
if (exitOnPageError) {
this.global.page.on('pageerror', handleError)
}
this.global.jestPlaywright = {
debug: async (): Promise<void> => {
// Run a debugger (in case Playwright has been launched with `{ devtools: true }`)
await this.global.page.evaluate(() => {
// eslint-disable-next-line no-debugger
debugger
})
// eslint-disable-next-line no-console
console.log('\n\n🕵️‍ Code is paused, press enter to resume')
// Run an infinite promise
return new Promise((resolve) => {
const { stdin } = process
const listening = stdin.listenerCount('data') > 0
const onKeyPress = (key: string): void => {
if (
key === KEYS.CONTROL_C ||
key === KEYS.CONTROL_D ||
key === KEYS.ENTER
) {
stdin.removeListener('data', onKeyPress)
if (!listening) {
if (stdin.isTTY) {
stdin.setRawMode(false)
if (device) {
const { viewport, userAgent } = playwright.devices[device]
contextOptions = { viewport, userAgent, ...contextOptions }
}
this.global.browserName = browserType
this.global.deviceName = device
this.global.browser = await getBrowserPerProcess(
playwrightInstance,
browserType,
config,
)
this.global.context = await this.global.browser.newContext(contextOptions)
this.global.page = await this.global.context.newPage()
if (exitOnPageError) {
this.global.page.on('pageerror', handleError)
}
this.global.jestPlaywright = {
debug: async (): Promise<void> => {
// Run a debugger (in case Playwright has been launched with `{ devtools: true }`)
await this.global.page.evaluate(() => {
// eslint-disable-next-line no-debugger
debugger
})
// eslint-disable-next-line no-console
console.log('\n\n🕵️‍ Code is paused, press enter to resume')
// Run an infinite promise
return new Promise((resolve) => {
const { stdin } = process
const listening = stdin.listenerCount('data') > 0
const onKeyPress = (key: string): void => {
if (
key === KEYS.CONTROL_C ||
key === KEYS.CONTROL_D ||
key === KEYS.ENTER
) {
stdin.removeListener('data', onKeyPress)
if (!listening) {
if (stdin.isTTY) {
stdin.setRawMode(false)
}
stdin.pause()
}
stdin.pause()
resolve()
}
resolve()
}
}
if (!listening) {
if (stdin.isTTY) {
stdin.setRawMode(true)
if (!listening) {
if (stdin.isTTY) {
stdin.setRawMode(true)
}
stdin.resume()
stdin.setEncoding('utf8')
}
stdin.resume()
stdin.setEncoding('utf8')
}
stdin.on('data', onKeyPress)
})
},
stdin.on('data', onKeyPress)
})
},
}
}
}

async handleTestEvent(event: Event, state: State): Promise<void> {
// Hack to set testTimeout for jestPlaywright debugging
if (
event.name === 'add_test' &&
event.fn &&
event.fn.toString().includes('jestPlaywright.debug()')
) {
// Set timeout to 4 days
state.testTimeout = 4 * 24 * 60 * 60 * 1000
async handleTestEvent(event: Event, state: State): Promise<void> {
// Hack to set testTimeout for jestPlaywright debugging
if (
event.name === 'add_test' &&
event.fn &&
event.fn.toString().includes('jestPlaywright.debug()')
) {
// Set timeout to 4 days
state.testTimeout = 4 * 24 * 60 * 60 * 1000
}
}
}

async teardown(jestConfig: JestConfig.InitialOptions = {}): Promise<void> {
const { page, context, browser } = this.global
if (page) {
page.removeListener('pageerror', handleError)
}
if (context) {
await context.close()
}
if (page) {
await page.close()
}
async teardown(jestConfig: JestConfig.InitialOptions = {}): Promise<void> {
const { page, browser } = this.global
if (page) {
page.removeListener('pageerror', handleError)
}

if (browser) {
await browser.close()
}
if (browser) {
await browser.close()
}

await super.teardown()
await super.teardown()

if (!jestConfig.watch && !jestConfig.watchAll && teardownServer) {
await teardownServer()
if (!jestConfig.watch && !jestConfig.watchAll && teardownServer) {
await teardownServer()
}
}
}
}

export default PlaywrightEnvironment
export default getPlaywrightEnv()
9 changes: 5 additions & 4 deletions src/PlaywrightRunner.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// @ts-nocheck
import JestRunner, {
import JestRunner from 'jest-runner'
import playwright from 'playwright-core'
import type {
Test,
TestRunnerContext,
TestWatcher,
Expand All @@ -8,9 +10,8 @@ import JestRunner, {
OnTestFailure,
TestRunnerOptions,
} from 'jest-runner'
import playwright from 'playwright-core'
import { Config as JestConfig } from '@jest/types'
import { BrowserType } from './constants'
import type { Config as JestConfig } from '@jest/types'
import type { BrowserType } from './types'
import {
checkBrowserEnv,
checkDeviceEnv,
Expand Down
35 changes: 1 addition & 34 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,11 @@
import {
BrowserTypeLaunchOptions,
BrowserNewContextOptions,
WebKitBrowser,
ChromiumBrowser,
FirefoxBrowser,
BrowserType as PlaywrightBrowserType,
BrowserTypeConnectOptions,
} from 'playwright-core'
import { JestDevServerOptions } from 'jest-dev-server'
import { Config } from './types'

export const IMPORT_KIND_PLAYWRIGHT = 'playwright'

export const CHROMIUM = 'chromium'
export const FIREFOX = 'firefox'
export const WEBKIT = 'webkit'

export type BrowserType = typeof CHROMIUM | typeof FIREFOX | typeof WEBKIT

export type GenericBrowser = PlaywrightBrowserType<
WebKitBrowser | ChromiumBrowser | FirefoxBrowser
>

export type SelectorType = {
script: string | Function | { path?: string; content?: string }
name: string
}

export type PlaywrightRequireType = BrowserType | typeof IMPORT_KIND_PLAYWRIGHT

export interface Config {
launchBrowserApp?: BrowserTypeLaunchOptions
context?: BrowserNewContextOptions
exitOnPageError: boolean
browsers?: BrowserType[]
devices?: string[]
server?: JestDevServerOptions
selectors?: SelectorType[]
connectBrowserApp?: BrowserTypeConnectOptions
}

export const DEFAULT_CONFIG: Config = {
launchBrowserApp: {},
context: {},
Expand Down
Loading

0 comments on commit d41ca51

Please sign in to comment.