mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-12 13:13:45 +01:00
[e2e] cut down flakeyness (#35896)
* [saas-e2e] conversion: wait for initial compile before looking at PDF * [eslint-plugin] s/before/beforeWithReRunOnTestRetry/ in e2e tests * [server-ce] remove filestore migration scripts and E2E tests * [server-ce] test: trim down upgrading tests to 5.x -> latest GitOrigin-RevId: 2426ec4a4f3d67a5e0e7850ea84a942ae4619f94
This commit is contained in:
@@ -22,5 +22,6 @@ module.exports = {
|
||||
'sorted-keys-in-locales': require('./sorted-keys-in-locales'),
|
||||
'locale-variables-match-en': require('./locale-variables-match-en'),
|
||||
'no-orphan-locale-keys': require('./no-orphan-locale-keys'),
|
||||
'no-mocha-before': require('./no-mocha-before'),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
const Path = require('node:path')
|
||||
|
||||
/**
|
||||
* Block bare mocha `before()` hooks in Cypress e2e suites.
|
||||
*
|
||||
* `before()` hooks run once per suite and do NOT re-run when a test retries,
|
||||
* while `beforeEach`-based hooks (like `beforeWithReRunOnTestRetry`) do.
|
||||
* Mixing the two also changes ordering: all `before()` hooks run ahead of
|
||||
* every `beforeEach`, not in source order. This matters in the Server Pro
|
||||
* e2e suite, where suites change the server config via `startWith()` while
|
||||
* test setup runs in `beforeWithReRunOnTestRetry()`: when a later suite
|
||||
* changes the config, both must re-run on retry and in registration order,
|
||||
* otherwise a retried test runs against setup or config belonging to a
|
||||
* different suite.
|
||||
*
|
||||
* The autofix rewrites `before(fn)` to `beforeWithReRunOnTestRetry(fn)` and
|
||||
* adds the helper import when missing. Configure the helper location (repo
|
||||
* root relative, no extension) via the `helperPath` option.
|
||||
*/
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: 'suggestion',
|
||||
docs: {
|
||||
description:
|
||||
'Disallow mocha before() hooks in favor of beforeWithReRunOnTestRetry()',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
helperPath: { type: 'string' },
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
],
|
||||
messages: {
|
||||
noMochaBefore:
|
||||
'before() hooks do not re-run when a test retries, so retried tests run against stale setup. Use beforeWithReRunOnTestRetry() instead.',
|
||||
},
|
||||
},
|
||||
create(context) {
|
||||
const helperPath = context.options[0]?.helperPath
|
||||
let lastImport = null
|
||||
let hasHelperImport = false
|
||||
|
||||
function helperImportSpecifier() {
|
||||
const absolute = Path.resolve(context.cwd, helperPath)
|
||||
let relative = Path.relative(
|
||||
Path.dirname(Path.resolve(context.cwd, context.filename)),
|
||||
absolute
|
||||
)
|
||||
relative = relative.split(Path.sep).join('/')
|
||||
if (!relative.startsWith('.')) {
|
||||
relative = `./${relative}`
|
||||
}
|
||||
return relative
|
||||
}
|
||||
|
||||
return {
|
||||
ImportDeclaration(node) {
|
||||
lastImport = node
|
||||
if (
|
||||
node.specifiers.some(
|
||||
specifier =>
|
||||
specifier.type === 'ImportSpecifier' &&
|
||||
specifier.imported.name === 'beforeWithReRunOnTestRetry'
|
||||
)
|
||||
) {
|
||||
hasHelperImport = true
|
||||
}
|
||||
},
|
||||
CallExpression(node) {
|
||||
if (
|
||||
node.callee.type !== 'Identifier' ||
|
||||
node.callee.name !== 'before'
|
||||
) {
|
||||
return
|
||||
}
|
||||
// Only the global mocha hook; skip locally defined/imported `before`
|
||||
const variable = context.sourceCode
|
||||
.getScope(node)
|
||||
.references.find(ref => ref.identifier === node.callee)?.resolved
|
||||
if (variable && variable.defs.length > 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const fixable = node.arguments.length === 1 && helperPath
|
||||
context.report({
|
||||
node: node.callee,
|
||||
messageId: 'noMochaBefore',
|
||||
fix: fixable
|
||||
? fixer => {
|
||||
const fixes = [
|
||||
fixer.replaceText(node.callee, 'beforeWithReRunOnTestRetry'),
|
||||
]
|
||||
if (!hasHelperImport) {
|
||||
const importText = `import { beforeWithReRunOnTestRetry } from '${helperImportSpecifier()}'`
|
||||
fixes.push(
|
||||
lastImport
|
||||
? fixer.insertTextAfter(lastImport, `\n${importText}`)
|
||||
: fixer.insertTextBeforeRange([0, 0], `${importText}\n`)
|
||||
)
|
||||
}
|
||||
return fixes
|
||||
}
|
||||
: undefined,
|
||||
})
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import noConsecutiveSpacesInLocales from '../../no-consecutive-spaces-in-locales
|
||||
import noStraightApostrophesInLocales from '../../no-straight-apostrophes-in-locales.js'
|
||||
import frenchTypographyInLocales from '../../french-typography-in-locales.js'
|
||||
import sortedKeysInLocales from '../../sorted-keys-in-locales.js'
|
||||
import noMochaBefore from '../../no-mocha-before.js'
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
|
||||
@@ -546,3 +547,81 @@ jsonRuleTester.run('french-typography-in-locales', frenchTypographyInLocales, {
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const noMochaBeforeOptions = [
|
||||
{ helperPath: 'server-ce/test/helpers/beforeWithReRunOnTestRetry' },
|
||||
]
|
||||
|
||||
ruleTester.run('no-mocha-before', noMochaBefore, {
|
||||
valid: [
|
||||
{
|
||||
code: `beforeEach(function () { setup() })`,
|
||||
options: noMochaBeforeOptions,
|
||||
},
|
||||
{
|
||||
code: `import { beforeWithReRunOnTestRetry } from './helpers/beforeWithReRunOnTestRetry'
|
||||
beforeWithReRunOnTestRetry(function () { setup() })`,
|
||||
options: noMochaBeforeOptions,
|
||||
},
|
||||
{
|
||||
// locally defined `before` is not the mocha hook
|
||||
code: `function before(fn) { fn() }
|
||||
before(function () { setup() })`,
|
||||
options: noMochaBeforeOptions,
|
||||
},
|
||||
],
|
||||
invalid: [
|
||||
{
|
||||
// rename + import added after the last existing import
|
||||
filename: 'server-ce/test/foo.spec.ts',
|
||||
code: `import { login } from './helpers/login'
|
||||
before(async function () { this.timeout(1000) })`,
|
||||
options: noMochaBeforeOptions,
|
||||
errors: 1,
|
||||
output: `import { login } from './helpers/login'
|
||||
import { beforeWithReRunOnTestRetry } from './helpers/beforeWithReRunOnTestRetry'
|
||||
beforeWithReRunOnTestRetry(async function () { this.timeout(1000) })`,
|
||||
},
|
||||
{
|
||||
// no imports yet: import inserted at the top
|
||||
filename: 'server-ce/test/foo.spec.ts',
|
||||
code: `before(function () { setup() })`,
|
||||
options: noMochaBeforeOptions,
|
||||
errors: 1,
|
||||
output: `import { beforeWithReRunOnTestRetry } from './helpers/beforeWithReRunOnTestRetry'
|
||||
beforeWithReRunOnTestRetry(function () { setup() })`,
|
||||
},
|
||||
{
|
||||
// helper already imported: rename only
|
||||
filename: 'server-ce/test/foo.spec.ts',
|
||||
code: `import { beforeWithReRunOnTestRetry } from './helpers/beforeWithReRunOnTestRetry'
|
||||
before(function () { setup() })`,
|
||||
options: noMochaBeforeOptions,
|
||||
errors: 1,
|
||||
output: `import { beforeWithReRunOnTestRetry } from './helpers/beforeWithReRunOnTestRetry'
|
||||
beforeWithReRunOnTestRetry(function () { setup() })`,
|
||||
},
|
||||
{
|
||||
// file inside helpers/: relative path has no directory prefix
|
||||
filename: 'server-ce/test/helpers/config.ts',
|
||||
code: `before(function () { setup() })`,
|
||||
options: noMochaBeforeOptions,
|
||||
errors: 1,
|
||||
output: `import { beforeWithReRunOnTestRetry } from './beforeWithReRunOnTestRetry'
|
||||
beforeWithReRunOnTestRetry(function () { setup() })`,
|
||||
},
|
||||
{
|
||||
// two-argument mocha form: report without autofix
|
||||
filename: 'server-ce/test/foo.spec.ts',
|
||||
code: `before('named hook', function () { setup() })`,
|
||||
options: noMochaBeforeOptions,
|
||||
errors: 1,
|
||||
},
|
||||
{
|
||||
// no helperPath configured: report without autofix
|
||||
filename: 'server-ce/test/foo.spec.ts',
|
||||
code: `before(function () { setup() })`,
|
||||
errors: 1,
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user