Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix maximum call stack size exceeded in Jasmine #8175

Merged
merged 2 commits into from Mar 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 6 additions & 2 deletions packages/wdio-runner/src/reporter.ts
Expand Up @@ -155,7 +155,9 @@ export default class BaseReporter {
ReporterClass = reporter as Reporters.ReporterClass
options.logFile = options.setLogFile
? options.setLogFile(this._cid, ReporterClass.name)
: this.getLogFile(ReporterClass.name)
: typeof options.logFile === 'string'
? options.logFile
: this.getLogFile(ReporterClass.name)
options.writeStream = this.getWriteStreamObject(ReporterClass.name)
return new ReporterClass(options)
}
Expand All @@ -178,7 +180,9 @@ export default class BaseReporter {
ReporterClass = initialisePlugin(reporter, 'reporter').default as Reporters.ReporterClass
options.logFile = options.setLogFile
? options.setLogFile(this._cid, reporter)
: this.getLogFile(reporter)
: typeof options.logFile === 'string'
? options.logFile
: this.getLogFile(reporter)
options.writeStream = this.getWriteStreamObject(reporter)
return new ReporterClass(options)
}
Expand Down
68 changes: 46 additions & 22 deletions packages/wdio-runner/tests/reporter.test.ts
@@ -1,4 +1,4 @@
import type { Capability } from '@wdio/config'
import type { Options, Capabilities } from '@wdio/types'

import BaseReporter from '../src/reporter'

Expand All @@ -14,7 +14,7 @@ class CustomReporter {
}
}

const capability: Capability = { browserName: 'foo' }
const capability: Capabilities.Capabilities = { browserName: 'foo' }

process.send = jest.fn()

Expand All @@ -26,7 +26,7 @@ describe('BaseReporter', () => {
'dot',
['dot', { foo: 'bar' }]
]
}, '0-0', capability)
} as Options.Testrunner, '0-0', capability)

expect(reporter['_reporters']).toHaveLength(2)
})
Expand All @@ -38,12 +38,30 @@ describe('BaseReporter', () => {
'dot',
['dot', { foo: 'bar' }]
]
}, '0-0', capability)
} as Options.Testrunner, '0-0', capability)

expect(reporter.getLogFile('foobar'))
.toMatch(/(\\|\/)foo(\\|\/)bar(\\|\/)wdio-0-0-foobar-reporter.log/)
})

it('can set custom logFile property', () => {
const reporter = new BaseReporter({
outputDir: '/foo/bar',
reporters: [
[CustomReporter, { foo: 'bar', logFile: '/barfoo.log' }],
['dot', { foo: 'bar', logFile: '/foobar.log' }]
],
capabilities: [capability]
} as Options.Testrunner, '0-0', capability)

// @ts-expect-error
expect(reporter['_reporters'][0].options.logFile)
.toBe('/barfoo.log')
// @ts-expect-error
expect(reporter['_reporters'][1].options.logFile)
.toBe('/foobar.log')
})

it('should output log file to custom outputDir', () => {
const reporter = new BaseReporter({
outputDir: '/foo/bar',
Expand All @@ -52,8 +70,9 @@ describe('BaseReporter', () => {
foo: 'bar',
outputDir: '/foo/bar/baz'
}]
]
}, '0-0', capability)
],
capabilities: [capability]
} as Options.Testrunner, '0-0', capability)
expect(reporter.getLogFile('dot'))
.toMatch(/(\\|\/)foo(\\|\/)bar(\\|\/)baz(\\|\/)wdio-0-0-dot-reporter.log/)
})
Expand All @@ -70,7 +89,7 @@ describe('BaseReporter', () => {
}
}]
]
}, '0-0', capability)
} as Options.Testrunner, '0-0', capability)

expect(reporter.getLogFile('dot'))
.toMatch(/(\\|\/)foo(\\|\/)bar(\\|\/)wdio-results-0-0.xml/)
Expand All @@ -87,7 +106,7 @@ describe('BaseReporter', () => {
outputFileFormat: 'foo'
}]
]
}, '0-0', capability)
} as Options.Testrunner, '0-0', capability)
}).toThrow('outputFileFormat must be a function')
})

Expand All @@ -97,7 +116,7 @@ describe('BaseReporter', () => {
'dot',
['dot', { foo: 'bar' }]
]
}, '0-0', capability)
} as Options.Testrunner, '0-0', capability)

expect(reporter.getLogFile('foobar')).toBe(undefined)
})
Expand All @@ -109,7 +128,7 @@ describe('BaseReporter', () => {
'dot',
['dot', { foo: 'bar' }]
]
}, '0-0', capability)
} as Options.Testrunner, '0-0', capability)

const payload = { foo: [1, 2, 3] }
reporter.emit('runner:start', payload)
Expand All @@ -127,7 +146,7 @@ describe('BaseReporter', () => {
'dot',
['dot', { foo: 'bar' }]
]
}, '0-0', capability)
} as Options.Testrunner, '0-0', capability)

const payload = { foo: [1, 2, 3] }
reporter.emit('test:fail', payload)
Expand All @@ -142,8 +161,9 @@ describe('BaseReporter', () => {
it('should allow to load custom reporters', () => {
const reporter = new BaseReporter({
outputDir: '/foo/bar',
reporters: [CustomReporter]
}, '0-0', capability)
reporters: [CustomReporter] as any,
capabilities: [capability]
} as Options.Testrunner, '0-0', capability)
expect(reporter['_reporters']).toHaveLength(1)
// @ts-ignore
expect(reporter['_reporters'][0].isCustom).toBe(true)
Expand All @@ -154,8 +174,9 @@ describe('BaseReporter', () => {
outputDir: '/foo/bar',
reporters: [[CustomReporter, {
outputDir: '/foo/baz/bar'
}]]
}, '0-0', capability)
}]] as any,
capabilities: [capability]
} as Options.Testrunner, '0-0', capability)

expect(reporter.getLogFile('CustomReporter')).toMatch(/(\\|\/)foo(\\|\/)baz(\\|\/)bar(\\|\/)wdio-0-0-CustomReporter-reporter.log/)
})
Expand All @@ -165,8 +186,9 @@ describe('BaseReporter', () => {
try {
new BaseReporter({
outputDir: '/foo/bar',
reporters: [{ foo: 'bar' }]
}, '0-0', capability)
reporters: [{ foo: 'bar' } as any],
capabilities: [capability]
} as Options.Testrunner, '0-0', capability)
} catch (err: any) {
expect(err.message).toBe('Invalid reporters config')
}
Expand All @@ -178,8 +200,9 @@ describe('BaseReporter', () => {
const start = Date.now()
const reporter = new BaseReporter({
outputDir: '/foo/bar',
reporters: [CustomReporter, CustomReporter]
}, '0-0', capability)
reporters: [CustomReporter, CustomReporter] as any,
capabilities: [capability]
} as Options.Testrunner, '0-0', capability)

// @ts-ignore test reporter param
setTimeout(() => (reporter['_reporters'][0].inSync = true), 100)
Expand All @@ -194,10 +217,11 @@ describe('BaseReporter', () => {

const reporter = new BaseReporter({
outputDir: '/foo/bar',
reporters: [CustomReporter],
reporters: [CustomReporter] as any,
reporterSyncInterval: 10,
reporterSyncTimeout: 100
}, '0-0', capability)
reporterSyncTimeout: 100,
capabilities: [capability]
} as Options.Testrunner, '0-0', capability)

// @ts-ignore test reporter param
setTimeout(() => (reporter['_reporters'][0].inSync = true), 112)
Expand Down
10 changes: 10 additions & 0 deletions packages/wdio-utils/src/shim.ts
Expand Up @@ -411,6 +411,16 @@ async function executeAsync(this: any, fn: Function, retries: Retries, args: any
asyncSpec = asyncSpecBefore
}

/**
* if test fail in jasmine (e.g. timeout) the finally statement
* would not be executed in async mode where fibers is not supported
*/
const nodeVersion = parseInt(process.version.slice(1).split('.').shift() || '0', 10)
if (isJasmine && nodeVersion >= 16) {
// @ts-ignore
global.expect = expectSync
}

return await result
} catch (err: any) {
if (retries.limit > retries.attempts) {
Expand Down
5 changes: 5 additions & 0 deletions tests/jasmine/test-timeout.js
Expand Up @@ -2,4 +2,9 @@ describe('Jasmine timeout test', () => {
it('should fail due to custom timeout', () => {
return new Promise((resolve) => setTimeout(resolve, 2000))
}, 1000)

// https://github.com/webdriverio/webdriverio/issues/8126
it('do not run into max callstack errors', () => {
expect(true).toBe(false)
})
})
29 changes: 27 additions & 2 deletions tests/smoke.runner.js
Expand Up @@ -29,8 +29,16 @@ async function runTests (tests) {
const testFilter = process.argv[2]

if (process.env.CI || testFilter) {
// sequential
const testsFiltered = testFilter ? tests.filter(test => test.name === testFilter) : tests

if (testsFiltered.length === 0) {
throw new Error(
`No test was selected! Smoke test "${testFilter}" ` +
`picked but only ${tests.map(test => test.name).join(', ')} available`
)
}

// sequential
for (let test of testsFiltered) {
await test()
}
Expand Down Expand Up @@ -113,15 +121,32 @@ const jasmineReporter = async () => {
* Jasmine timeout test
*/
const jasmineTimeout = async () => {
const logFile = path.join(__dirname, 'jasmineTimeout.spec.log')
const err = await launch(
path.resolve(__dirname, 'helpers', 'config.js'),
{
specs: [path.resolve(__dirname, 'jasmine', 'test-timeout.js')],
reporters: [['spec', { outputDir: __dirname }]],
reporters: [
['spec', {
outputDir: __dirname,
stdout: false,
logFile
}]
],
framework: 'jasmine'
}
).catch(err => err)
assert.strictEqual(err.message, 'Smoke test failed')

const specLogs = (await fs.readFile(logFile)).toString()
assert.ok(
specLogs.includes('Error: Timeout - Async function did not complete within 1000ms (custom timeout)'),
'spec was not failing due to timeout error'
)
assert.ok(
!specLogs.includes('RangeError: Maximum call stack size exceeded'),
'spec was failing due to unexpected "Maximum call stack size exceeded"'
)
}

/**
Expand Down