diff --git a/packages/wdio-utils/src/test-framework/testFnWrapper.ts b/packages/wdio-utils/src/test-framework/testFnWrapper.ts index 1d407e65eb3..7b63dd06792 100644 --- a/packages/wdio-utils/src/test-framework/testFnWrapper.ts +++ b/packages/wdio-utils/src/test-framework/testFnWrapper.ts @@ -10,6 +10,13 @@ import type { JasmineContext } from './types' +const STACKTRACE_FILTER = [ + 'node_modules/webdriver/', + 'node_modules/webdriverio/', + 'node_modules/@wdio/', + '(internal/process/task', +] + /** * wraps test framework spec/hook function with WebdriverIO before/after hooks * @@ -77,6 +84,10 @@ export const testFrameworkFnWrapper = async function ( try { result = await promise } catch (err: any) { + if (err.stack) { + err.stack = filterStackTrace(err.stack) + } + error = err } const duration = Date.now() - testStart @@ -106,3 +117,15 @@ export const testFrameworkFnWrapper = async function ( } return result } + +/** + * Filter out internal stacktraces. exporting to allow testing of the function + * @param {string} stack Stacktrace + * @returns {string} + */ +export const filterStackTrace = (stack: string): string => { + return stack + .split('\n') + .filter(line => !STACKTRACE_FILTER.some(l => line.includes(l))) + .join('\n') +} diff --git a/packages/wdio-utils/tests/test-framework/testFnWrapper-async.test.js b/packages/wdio-utils/tests/test-framework/testFnWrapper-async.test.js index 9aa2c8d6d49..3fa9606472c 100644 --- a/packages/wdio-utils/tests/test-framework/testFnWrapper-async.test.js +++ b/packages/wdio-utils/tests/test-framework/testFnWrapper-async.test.js @@ -1,5 +1,5 @@ import * as shim from '../../src/shim' -import { testFnWrapper } from '../../src/test-framework/testFnWrapper' +import { testFnWrapper, filterStackTrace } from '../../src/test-framework/testFnWrapper' jest.mock('../../src/shim', () => ({ executeHooksWithArgs: jest.fn(), @@ -91,3 +91,39 @@ describe('testFnWrapper', () => { executeHooksWithArgs.mockClear() }) }) + +describe('filterStackTrace', () => { + it('should remove internal webdriverio lines only', () => { + const stackTraces = [ + { + fullStack : ` + at Context. (/foo/bar/baz/example.e2e.js:27:9) + at Context.executeAsync (/foo/bar/baz/node_modules/@wdio/utils/build/shim.js:331:27) + at Context.testFrameworkFnWrapper (/foo/bar/baz/node_modules/@wdio/utils/build/test-framework/testFnWrapper.js:50:32) + at processTicksAndRejections (internal/process/task_queues.js:95:5) + `, + filteredStack : ` + at Context. (/foo/bar/baz/example.e2e.js:27:9) + ` + }, + { + fullStack : ` + at implicitWait (/foo/bar/node_modules/webdriverio/build/utils/implicitWait.js:34:19) + at async Element.elementErrorHandlerCallbackFn (/foo/bar/node_modules/webdriverio/build/middlewares.js:20:29) + at async Element.wrapCommandFn (/foo/bar/node_modules/@wdio/utils/build/shim.js:137:29) + at async Element.wrapCommandFn (/foo/bar/node_modules/some/fake/lib/bar.js:137:29) + at async Context. (/foo/bar/test/specs/example.e2e.ts:8:9) + at processTicksAndRejections (internal/process/task_queues.js:95:5) + `, + filteredStack : ` + at async Element.wrapCommandFn (/foo/bar/node_modules/some/fake/lib/bar.js:137:29) + at async Context. (/foo/bar/test/specs/example.e2e.ts:8:9) + ` + } + ] + + for (const { fullStack, filteredStack } of stackTraces) { + expect(filteredStack).toBe(filterStackTrace(fullStack)) + } + }) +})