-
Notifications
You must be signed in to change notification settings - Fork 791
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add support for jest 29 in stencil. this commit is a fast-follow to #4979 (d3aa539), which adds support for jest 28. as such, there will be many similarlities between the two pieces of code. STENCIL-956
- Loading branch information
1 parent
d3aa539
commit 19d85de
Showing
29 changed files
with
5,535 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
import type { Config } from '@jest/types'; | ||
import type * as d from '@stencil/core/internal'; | ||
import { isString } from '@utils'; | ||
|
||
import { Jest29Stencil } from './jest-facade'; | ||
|
||
/** | ||
* Builds the `argv` to be used when programmatically invoking the Jest CLI | ||
* @param config the Stencil config to use while generating Jest CLI arguments | ||
* @returns the arguments to pass to the Jest CLI, wrapped in an object | ||
*/ | ||
export function buildJestArgv(config: d.ValidatedConfig): Config.Argv { | ||
const yargs = require('yargs'); | ||
|
||
const knownArgs = config.flags.knownArgs.slice(); | ||
|
||
if (!knownArgs.some((a) => a.startsWith('--max-workers') || a.startsWith('--maxWorkers'))) { | ||
knownArgs.push(`--max-workers=${config.maxConcurrentWorkers}`); | ||
} | ||
|
||
if (config.flags.devtools) { | ||
knownArgs.push('--runInBand'); | ||
} | ||
|
||
// we combine the modified args and the unknown args here and declare the | ||
// result read only, providing some type system-level assurance that we won't | ||
// mutate it after this point. | ||
// | ||
// We want that assurance because Jest likes to have any filepath match | ||
// patterns at the end of the args it receives. Those args are going to be | ||
// found in our `unknownArgs`, so while we want to do some stuff in this | ||
// function that adds to `knownArgs` we need a guarantee that all of the | ||
// `unknownArgs` are _after_ all the `knownArgs` in the array we end up | ||
// generating the Jest configuration from. | ||
const args: ReadonlyArray<string> = [...knownArgs, ...config.flags.unknownArgs]; | ||
|
||
config.logger.info(config.logger.magenta(`jest args: ${args.join(' ')}`)); | ||
|
||
const jestArgv = yargs(args).argv as Config.Argv; | ||
jestArgv.config = buildJestConfig(config); | ||
|
||
if (typeof jestArgv.maxWorkers === 'string') { | ||
try { | ||
jestArgv.maxWorkers = parseInt(jestArgv.maxWorkers, 10); | ||
} catch (e) {} | ||
} | ||
|
||
if (typeof jestArgv.ci === 'string') { | ||
jestArgv.ci = jestArgv.ci === 'true' || jestArgv.ci === ''; | ||
} | ||
|
||
return jestArgv; | ||
} | ||
|
||
/** | ||
* Generate a Jest run configuration to be used as a part of the `argv` passed to the Jest CLI when it is invoked | ||
* programmatically | ||
* @param config the Stencil config to use while generating Jest CLI arguments | ||
* @returns the Jest Config to attach to the `argv` argument | ||
*/ | ||
export function buildJestConfig(config: d.ValidatedConfig): string { | ||
const stencilConfigTesting = config.testing; | ||
const jestDefaults: Config.DefaultOptions = require('jest-config').defaults; | ||
|
||
const validJestConfigKeys = Object.keys(jestDefaults); | ||
|
||
const jestConfig: d.JestConfig = {}; | ||
|
||
Object.keys(stencilConfigTesting).forEach((key) => { | ||
if (validJestConfigKeys.includes(key)) { | ||
(jestConfig as any)[key] = (stencilConfigTesting as any)[key]; | ||
} | ||
}); | ||
|
||
jestConfig.rootDir = config.rootDir; | ||
|
||
if (isString(stencilConfigTesting.collectCoverage)) { | ||
jestConfig.collectCoverage = stencilConfigTesting.collectCoverage; | ||
} | ||
if (Array.isArray(stencilConfigTesting.collectCoverageFrom)) { | ||
jestConfig.collectCoverageFrom = stencilConfigTesting.collectCoverageFrom; | ||
} | ||
if (isString(stencilConfigTesting.coverageDirectory)) { | ||
jestConfig.coverageDirectory = stencilConfigTesting.coverageDirectory; | ||
} | ||
if (stencilConfigTesting.coverageThreshold) { | ||
jestConfig.coverageThreshold = stencilConfigTesting.coverageThreshold; | ||
} | ||
if (isString(stencilConfigTesting.globalSetup)) { | ||
jestConfig.globalSetup = stencilConfigTesting.globalSetup; | ||
} | ||
if (isString(stencilConfigTesting.globalTeardown)) { | ||
jestConfig.globalTeardown = stencilConfigTesting.globalTeardown; | ||
} | ||
if (isString(stencilConfigTesting.preset)) { | ||
jestConfig.preset = stencilConfigTesting.preset; | ||
} | ||
if (stencilConfigTesting.projects) { | ||
jestConfig.projects = stencilConfigTesting.projects; | ||
} | ||
if (Array.isArray(stencilConfigTesting.reporters)) { | ||
jestConfig.reporters = stencilConfigTesting.reporters; | ||
} | ||
if (isString(stencilConfigTesting.testResultsProcessor)) { | ||
jestConfig.testResultsProcessor = stencilConfigTesting.testResultsProcessor; | ||
} | ||
if (stencilConfigTesting.transform) { | ||
jestConfig.transform = stencilConfigTesting.transform; | ||
} | ||
if (stencilConfigTesting.verbose) { | ||
jestConfig.verbose = stencilConfigTesting.verbose; | ||
} | ||
|
||
jestConfig.testRunner = new Jest29Stencil().getDefaultJestRunner(); | ||
|
||
return JSON.stringify(jestConfig); | ||
} | ||
|
||
export function getProjectListFromCLIArgs(config: d.ValidatedConfig, argv: Config.Argv): string[] { | ||
const projects = argv.projects ? argv.projects : []; | ||
|
||
projects.push(config.rootDir); | ||
|
||
return projects; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import type { Circus } from '@jest/types'; | ||
import type { E2EProcessEnv, JestEnvironmentGlobal } from '@stencil/core/internal'; | ||
|
||
import { connectBrowser, disconnectBrowser, newBrowserPage } from '../../puppeteer/puppeteer-browser'; | ||
|
||
export function createJestPuppeteerEnvironment() { | ||
const NodeEnvironment = require('jest-environment-node').TestEnvironment; | ||
const JestEnvironment = class extends NodeEnvironment { | ||
global: JestEnvironmentGlobal; | ||
browser: any = null; | ||
pages: any[] = []; | ||
testPath: string | null = null; | ||
|
||
constructor(config: any, context: any) { | ||
super(config, context); | ||
this.testPath = context.testPath; | ||
} | ||
|
||
async setup() { | ||
if ((process.env as E2EProcessEnv).__STENCIL_E2E_TESTS__ === 'true') { | ||
this.global.__NEW_TEST_PAGE__ = this.newPuppeteerPage.bind(this); | ||
this.global.__CLOSE_OPEN_PAGES__ = this.closeOpenPages.bind(this); | ||
} | ||
} | ||
|
||
/** | ||
* Jest Circus hook for capturing events. | ||
* | ||
* We use this lifecycle hook to capture information about the currently running test in the event that it is a | ||
* Jest-Stencil screenshot test, so that we may accurately report on it. | ||
* | ||
* @param event the captured runtime event | ||
*/ | ||
async handleTestEvent(event: Circus.AsyncEvent): Promise<void> { | ||
// The 'parent' of a top-level describe block in a Jest block has one more 'parent', which is this string. | ||
// It is not exported by Jest, and is therefore copied here to exclude it from the fully qualified test name. | ||
const ROOT_DESCRIBE_BLOCK = 'ROOT_DESCRIBE_BLOCK'; | ||
if (event.name === 'test_start') { | ||
const eventTest = event.test; | ||
|
||
/** | ||
* We need to build the full name of the test for screenshot tests. | ||
* We do this as a test name can be the same across multiple tests - e.g. `it('renders', () => {...});`. | ||
* While this does not necessarily guarantee the generated name will be unique, it matches previous Jest-Stencil | ||
* screenshot behavior. | ||
*/ | ||
let fullName = eventTest.name; | ||
let currentParent: Circus.DescribeBlock | undefined = eventTest.parent; | ||
// For each parent block (`describe('suite description', () => {...}`), grab the suite description and prepend | ||
// it to the running name. | ||
while (currentParent && currentParent.name && currentParent.name != ROOT_DESCRIBE_BLOCK) { | ||
fullName = `${currentParent.name} ${fullName}`; | ||
currentParent = currentParent.parent; | ||
} | ||
// Set the current spec for us to inspect for using the default reporter in screenshot tests. | ||
this.global.currentSpec = { | ||
// the event's test's name is analogous to the original description in earlier versions of jest | ||
description: eventTest.name, | ||
fullName, | ||
testPath: this.testPath, | ||
}; | ||
} | ||
} | ||
async newPuppeteerPage() { | ||
if (!this.browser) { | ||
// load the browser and page on demand | ||
this.browser = await connectBrowser(); | ||
} | ||
|
||
const page = await newBrowserPage(this.browser); | ||
this.pages.push(page); | ||
// during E2E tests, we can safely assume that the current environment is a `E2EProcessEnv` | ||
const env: E2EProcessEnv = process.env as E2EProcessEnv; | ||
if (typeof env.__STENCIL_DEFAULT_TIMEOUT__ === 'string') { | ||
page.setDefaultTimeout(parseInt(env.__STENCIL_DEFAULT_TIMEOUT__, 10)); | ||
} | ||
return page; | ||
} | ||
|
||
async closeOpenPages() { | ||
await Promise.all(this.pages.map((page) => page.close())); | ||
this.pages.length = 0; | ||
} | ||
|
||
async teardown() { | ||
await super.teardown(); | ||
await this.closeOpenPages(); | ||
await disconnectBrowser(this.browser); | ||
this.browser = null; | ||
} | ||
|
||
getVmContext() { | ||
return super.getVmContext(); | ||
} | ||
}; | ||
|
||
return JestEnvironment; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
// @ts-ignore - without importing this, we get a TypeScript error, "TS4053". | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
import type { Config } from '@jest/types'; | ||
|
||
import { JestFacade } from '../jest-facade'; | ||
import { createJestPuppeteerEnvironment } from './jest-environment'; | ||
import { jestPreprocessor } from './jest-preprocessor'; | ||
import { preset } from './jest-preset'; | ||
import { createTestRunner } from './jest-runner'; | ||
import { runJest } from './jest-runner'; | ||
import { runJestScreenshot } from './jest-screenshot'; | ||
import { jestSetupTestFramework } from './jest-setup-test-framework'; | ||
|
||
/** | ||
* `JestFacade` implementation for communicating between this directory's version of Jest and Stencil | ||
*/ | ||
export class Jest29Stencil implements JestFacade { | ||
getJestCliRunner() { | ||
return runJest; | ||
} | ||
|
||
getRunJestScreenshot() { | ||
return runJestScreenshot; | ||
} | ||
|
||
getDefaultJestRunner() { | ||
return 'jest-circus'; | ||
} | ||
|
||
getCreateJestPuppeteerEnvironment() { | ||
return createJestPuppeteerEnvironment; | ||
} | ||
|
||
getJestPreprocessor() { | ||
return jestPreprocessor; | ||
} | ||
|
||
getCreateJestTestRunner() { | ||
return createTestRunner; | ||
} | ||
|
||
getJestSetupTestFramework() { | ||
return jestSetupTestFramework; | ||
} | ||
|
||
getJestPreset() { | ||
return preset; | ||
} | ||
} |
Oops, something went wrong.