|
1 | 1 | /* eslint-disable no-console */ |
2 | 2 | /* eslint-disable @typescript-eslint/ban-ts-ignore */ |
3 | | -import NodeEnvironment from 'jest-environment-node' |
4 | | -import { Config as JestConfig } from '@jest/types' |
5 | | -import { Event, State } from 'jest-circus' |
| 3 | +import type { Config as JestConfig } from '@jest/types' |
| 4 | +import type { Event, State } from 'jest-circus' |
| 5 | +import type { Browser } from 'playwright-core' |
| 6 | +import playwright from 'playwright-core' |
| 7 | +import type { Config, GenericBrowser, BrowserType } from './types' |
| 8 | +import { CHROMIUM, IMPORT_KIND_PLAYWRIGHT } from './constants' |
6 | 9 | import { |
7 | 10 | getBrowserType, |
8 | 11 | getDeviceType, |
9 | 12 | getPlaywrightInstance, |
10 | 13 | readConfig, |
11 | 14 | readPackage, |
12 | 15 | } from './utils' |
13 | | -import { |
14 | | - Config, |
15 | | - CHROMIUM, |
16 | | - GenericBrowser, |
17 | | - IMPORT_KIND_PLAYWRIGHT, |
18 | | - BrowserType, |
19 | | -} from './constants' |
20 | | -import playwright, { Browser } from 'playwright-core' |
21 | 16 |
|
22 | 17 | const handleError = (error: Error): void => { |
23 | 18 | process.emit('uncaughtException', error) |
@@ -64,154 +59,156 @@ const getBrowserPerProcess = async ( |
64 | 59 | } |
65 | 60 | } |
66 | 61 |
|
67 | | -class PlaywrightEnvironment extends NodeEnvironment { |
68 | | - private _config: JestConfig.ProjectConfig |
69 | | - constructor(config: JestConfig.ProjectConfig) { |
70 | | - super(config) |
71 | | - this._config = config |
72 | | - } |
| 62 | +export const getPlaywrightEnv = (basicEnv = 'node') => { |
| 63 | + // eslint-disable-next-line @typescript-eslint/no-var-requires |
| 64 | + const RootEnv = require(basicEnv === 'node' |
| 65 | + ? 'jest-environment-node' |
| 66 | + : 'jest-environment-jsdom') |
73 | 67 |
|
74 | | - async setup(): Promise<void> { |
75 | | - const config = await readConfig(this._config.rootDir) |
76 | | - //@ts-ignore |
77 | | - const browserType = getBrowserType(this._config.browserName) |
78 | | - const { context, exitOnPageError, server, selectors } = config |
79 | | - const playwrightPackage = await readPackage() |
80 | | - if (playwrightPackage === IMPORT_KIND_PLAYWRIGHT) { |
81 | | - // eslint-disable-next-line @typescript-eslint/no-var-requires |
82 | | - const playwright = require('playwright') |
83 | | - if (selectors) { |
84 | | - await Promise.all( |
85 | | - selectors.map(({ name, script }) => { |
86 | | - return playwright.selectors.register(name, script) |
87 | | - }), |
88 | | - ) |
89 | | - } |
| 68 | + return class PlaywrightEnvironment extends RootEnv { |
| 69 | + private _config: JestConfig.ProjectConfig |
| 70 | + |
| 71 | + constructor(config: JestConfig.ProjectConfig) { |
| 72 | + super(config) |
| 73 | + this._config = config |
90 | 74 | } |
91 | | - //@ts-ignore |
92 | | - const device = getDeviceType(this._config.device) |
93 | | - const playwrightInstance = await getPlaywrightInstance( |
94 | | - playwrightPackage, |
95 | | - browserType, |
96 | | - ) |
97 | | - let contextOptions = context |
98 | | - |
99 | | - if (server) { |
100 | | - // eslint-disable-next-line @typescript-eslint/no-var-requires |
101 | | - const devServer = require('jest-dev-server') |
102 | | - const { setup, ERROR_TIMEOUT, ERROR_NO_COMMAND } = devServer |
103 | | - teardownServer = devServer.teardown |
104 | | - try { |
105 | | - await setup(server) |
106 | | - } catch (error) { |
107 | | - if (error.code === ERROR_TIMEOUT) { |
108 | | - logMessage({ |
109 | | - message: error.message, |
110 | | - action: 'can set "server.launchTimeout"', |
111 | | - }) |
| 75 | + |
| 76 | + async setup(): Promise<void> { |
| 77 | + const config = await readConfig(this._config.rootDir) |
| 78 | + //@ts-ignore |
| 79 | + const browserType = getBrowserType(this._config.browserName) |
| 80 | + const { context, exitOnPageError, server, selectors } = config |
| 81 | + const playwrightPackage = await readPackage() |
| 82 | + if (playwrightPackage === IMPORT_KIND_PLAYWRIGHT) { |
| 83 | + // eslint-disable-next-line @typescript-eslint/no-var-requires |
| 84 | + const playwright = require('playwright') |
| 85 | + if (selectors) { |
| 86 | + await Promise.all( |
| 87 | + selectors.map(({ name, script }) => { |
| 88 | + return playwright.selectors.register(name, script) |
| 89 | + }), |
| 90 | + ) |
112 | 91 | } |
113 | | - if (error.code === ERROR_NO_COMMAND) { |
114 | | - logMessage({ |
115 | | - message: error.message, |
116 | | - action: 'must set "server.command"', |
117 | | - }) |
| 92 | + } |
| 93 | + //@ts-ignore |
| 94 | + const device = getDeviceType(this._config.device) |
| 95 | + const playwrightInstance = await getPlaywrightInstance( |
| 96 | + playwrightPackage, |
| 97 | + browserType, |
| 98 | + ) |
| 99 | + let contextOptions = context |
| 100 | + |
| 101 | + if (server) { |
| 102 | + // eslint-disable-next-line @typescript-eslint/no-var-requires |
| 103 | + const devServer = require('jest-dev-server') |
| 104 | + const { setup, ERROR_TIMEOUT, ERROR_NO_COMMAND } = devServer |
| 105 | + teardownServer = devServer.teardown |
| 106 | + try { |
| 107 | + await setup(server) |
| 108 | + } catch (error) { |
| 109 | + if (error.code === ERROR_TIMEOUT) { |
| 110 | + logMessage({ |
| 111 | + message: error.message, |
| 112 | + action: 'can set "server.launchTimeout"', |
| 113 | + }) |
| 114 | + } |
| 115 | + if (error.code === ERROR_NO_COMMAND) { |
| 116 | + logMessage({ |
| 117 | + message: error.message, |
| 118 | + action: 'must set "server.command"', |
| 119 | + }) |
| 120 | + } |
| 121 | + throw error |
118 | 122 | } |
119 | | - throw error |
120 | 123 | } |
121 | | - } |
122 | 124 |
|
123 | | - if (device) { |
124 | | - const { viewport, userAgent } = playwright.devices[device] |
125 | | - contextOptions = { viewport, userAgent, ...contextOptions } |
126 | | - } |
127 | | - this.global.browserName = browserType |
128 | | - this.global.deviceName = device |
129 | | - this.global.browser = await getBrowserPerProcess( |
130 | | - playwrightInstance, |
131 | | - browserType, |
132 | | - config, |
133 | | - ) |
134 | | - this.global.context = await this.global.browser.newContext(contextOptions) |
135 | | - this.global.page = await this.global.context.newPage() |
136 | | - if (exitOnPageError) { |
137 | | - this.global.page.on('pageerror', handleError) |
138 | | - } |
139 | | - this.global.jestPlaywright = { |
140 | | - debug: async (): Promise<void> => { |
141 | | - // Run a debugger (in case Playwright has been launched with `{ devtools: true }`) |
142 | | - await this.global.page.evaluate(() => { |
143 | | - // eslint-disable-next-line no-debugger |
144 | | - debugger |
145 | | - }) |
146 | | - // eslint-disable-next-line no-console |
147 | | - console.log('\n\n🕵️ Code is paused, press enter to resume') |
148 | | - // Run an infinite promise |
149 | | - return new Promise((resolve) => { |
150 | | - const { stdin } = process |
151 | | - const listening = stdin.listenerCount('data') > 0 |
152 | | - const onKeyPress = (key: string): void => { |
153 | | - if ( |
154 | | - key === KEYS.CONTROL_C || |
155 | | - key === KEYS.CONTROL_D || |
156 | | - key === KEYS.ENTER |
157 | | - ) { |
158 | | - stdin.removeListener('data', onKeyPress) |
159 | | - if (!listening) { |
160 | | - if (stdin.isTTY) { |
161 | | - stdin.setRawMode(false) |
| 125 | + if (device) { |
| 126 | + const { viewport, userAgent } = playwright.devices[device] |
| 127 | + contextOptions = { viewport, userAgent, ...contextOptions } |
| 128 | + } |
| 129 | + this.global.browserName = browserType |
| 130 | + this.global.deviceName = device |
| 131 | + this.global.browser = await getBrowserPerProcess( |
| 132 | + playwrightInstance, |
| 133 | + browserType, |
| 134 | + config, |
| 135 | + ) |
| 136 | + this.global.context = await this.global.browser.newContext(contextOptions) |
| 137 | + this.global.page = await this.global.context.newPage() |
| 138 | + if (exitOnPageError) { |
| 139 | + this.global.page.on('pageerror', handleError) |
| 140 | + } |
| 141 | + this.global.jestPlaywright = { |
| 142 | + debug: async (): Promise<void> => { |
| 143 | + // Run a debugger (in case Playwright has been launched with `{ devtools: true }`) |
| 144 | + await this.global.page.evaluate(() => { |
| 145 | + // eslint-disable-next-line no-debugger |
| 146 | + debugger |
| 147 | + }) |
| 148 | + // eslint-disable-next-line no-console |
| 149 | + console.log('\n\n🕵️ Code is paused, press enter to resume') |
| 150 | + // Run an infinite promise |
| 151 | + return new Promise((resolve) => { |
| 152 | + const { stdin } = process |
| 153 | + const listening = stdin.listenerCount('data') > 0 |
| 154 | + const onKeyPress = (key: string): void => { |
| 155 | + if ( |
| 156 | + key === KEYS.CONTROL_C || |
| 157 | + key === KEYS.CONTROL_D || |
| 158 | + key === KEYS.ENTER |
| 159 | + ) { |
| 160 | + stdin.removeListener('data', onKeyPress) |
| 161 | + if (!listening) { |
| 162 | + if (stdin.isTTY) { |
| 163 | + stdin.setRawMode(false) |
| 164 | + } |
| 165 | + stdin.pause() |
162 | 166 | } |
163 | | - stdin.pause() |
| 167 | + resolve() |
164 | 168 | } |
165 | | - resolve() |
166 | 169 | } |
167 | | - } |
168 | | - if (!listening) { |
169 | | - if (stdin.isTTY) { |
170 | | - stdin.setRawMode(true) |
| 170 | + if (!listening) { |
| 171 | + if (stdin.isTTY) { |
| 172 | + stdin.setRawMode(true) |
| 173 | + } |
| 174 | + stdin.resume() |
| 175 | + stdin.setEncoding('utf8') |
171 | 176 | } |
172 | | - stdin.resume() |
173 | | - stdin.setEncoding('utf8') |
174 | | - } |
175 | | - stdin.on('data', onKeyPress) |
176 | | - }) |
177 | | - }, |
| 177 | + stdin.on('data', onKeyPress) |
| 178 | + }) |
| 179 | + }, |
| 180 | + } |
178 | 181 | } |
179 | | - } |
180 | 182 |
|
181 | | - async handleTestEvent(event: Event, state: State): Promise<void> { |
182 | | - // Hack to set testTimeout for jestPlaywright debugging |
183 | | - if ( |
184 | | - event.name === 'add_test' && |
185 | | - event.fn && |
186 | | - event.fn.toString().includes('jestPlaywright.debug()') |
187 | | - ) { |
188 | | - // Set timeout to 4 days |
189 | | - state.testTimeout = 4 * 24 * 60 * 60 * 1000 |
| 183 | + async handleTestEvent(event: Event, state: State): Promise<void> { |
| 184 | + // Hack to set testTimeout for jestPlaywright debugging |
| 185 | + if ( |
| 186 | + event.name === 'add_test' && |
| 187 | + event.fn && |
| 188 | + event.fn.toString().includes('jestPlaywright.debug()') |
| 189 | + ) { |
| 190 | + // Set timeout to 4 days |
| 191 | + state.testTimeout = 4 * 24 * 60 * 60 * 1000 |
| 192 | + } |
190 | 193 | } |
191 | | - } |
192 | 194 |
|
193 | | - async teardown(jestConfig: JestConfig.InitialOptions = {}): Promise<void> { |
194 | | - const { page, context, browser } = this.global |
195 | | - if (page) { |
196 | | - page.removeListener('pageerror', handleError) |
197 | | - } |
198 | | - if (context) { |
199 | | - await context.close() |
200 | | - } |
201 | | - if (page) { |
202 | | - await page.close() |
203 | | - } |
| 195 | + async teardown(jestConfig: JestConfig.InitialOptions = {}): Promise<void> { |
| 196 | + const { page, browser } = this.global |
| 197 | + if (page) { |
| 198 | + page.removeListener('pageerror', handleError) |
| 199 | + } |
204 | 200 |
|
205 | | - if (browser) { |
206 | | - await browser.close() |
207 | | - } |
| 201 | + if (browser) { |
| 202 | + await browser.close() |
| 203 | + } |
208 | 204 |
|
209 | | - await super.teardown() |
| 205 | + await super.teardown() |
210 | 206 |
|
211 | | - if (!jestConfig.watch && !jestConfig.watchAll && teardownServer) { |
212 | | - await teardownServer() |
| 207 | + if (!jestConfig.watch && !jestConfig.watchAll && teardownServer) { |
| 208 | + await teardownServer() |
| 209 | + } |
213 | 210 | } |
214 | 211 | } |
215 | 212 | } |
216 | 213 |
|
217 | | -export default PlaywrightEnvironment |
| 214 | +export default getPlaywrightEnv() |
0 commit comments