Skip to content

fix: Align Jasmine typing + support jasmine asymetrics matchers + other types fixes#2018

Open
dprevost-LMI wants to merge 60 commits into
webdriverio:mainfrom
dprevost-LMI:use-expect-async-matchers-with-jasmine
Open

fix: Align Jasmine typing + support jasmine asymetrics matchers + other types fixes#2018
dprevost-LMI wants to merge 60 commits into
webdriverio:mainfrom
dprevost-LMI:use-expect-async-matchers-with-jasmine

Conversation

@dprevost-LMI

@dprevost-LMI dprevost-LMI commented Feb 4, 2026

Copy link
Copy Markdown
Contributor

Fixes #1893
Fixes #1407 for real

Highlights:

  • jasmine-expect-global.test-d provides proper Jasmine typing to use expect asynchronously with wdio/jasmine-framework
    • withContext is support for sync & async matchers
    • sync matchers are also async, following the hack of adding all sync matchers on expect forced to be expectAsync
    • Later to move into wdio/jasmine-framework
  • jasmine.stringContaining now working with any text-based wdio matcher

TODOs

  • Check and/or document if supporting Jasmine asymetrics matcher under expec-wdio import is needed

Waiting

@christian-bromann

Copy link
Copy Markdown
Member

@dprevost-LMI thanks for this effort, let me know the status of this and when I can review.

@dprevost-LMI dprevost-LMI force-pushed the use-expect-async-matchers-with-jasmine branch from 8ac2038 to 4648bd6 Compare March 8, 2026 15:59
Comment thread playgrounds/jest/jest.setup.after-env.ts Outdated
@dprevost-LMI dprevost-LMI force-pushed the use-expect-async-matchers-with-jasmine branch from 34cb319 to b864314 Compare March 8, 2026 17:59
@dprevost-LMI dprevost-LMI force-pushed the use-expect-async-matchers-with-jasmine branch from b864314 to 5bb0b0c Compare March 14, 2026 12:28
@dprevost-LMI dprevost-LMI force-pushed the use-expect-async-matchers-with-jasmine branch from b129c66 to fa34827 Compare June 7, 2026 15:39
@dprevost-LMI dprevost-LMI marked this pull request as ready for review June 8, 2026 14:38
@greptile-apps

greptile-apps Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds Jasmine asymmetric-matcher support (primarily jasmine.stringContaining) to all text-based WDIO matchers, fixes Jasmine typing for @wdio/jasmine-framework with proper async augmentation, and cleans up how asymmetric matcher values are extracted by introducing getAsymmetricMatcherValue to handle both the Wdio (.sample) and Jasmine (.expected) shapes.

  • src/utils.ts — replaces the $$typeof-based isAsymmetricMatcher with a shape-based check, adds JasmineAsymmetricMatcher detection helpers, and threads Jasmine matcher support through compareText / compareTextWithArray; isStrictlyStringContainingMatcher contains a substring-check bug that inverts the ignoreCase transformation for expect.not.stringContaining() matchers.
  • jasmine-wdio-expect-async.d.ts — introduces JasmineExpect with withContext, converts sync Jasmine matchers to async, and overrides the global expect for @wdio/jasmine-framework consumers.
  • src/matchers/mock/toBeRequestedWith.ts — removes the local isMatcher helper and delegates to the shared isAsymmetricMatcher / getAsymmetricMatcherValue utilities, correctly supporting both matcher shapes in failure messages.

Confidence Score: 3/5

Not safe to merge as-is — the ignoreCase path for negative Jest/expect matchers is broken and pre-existing tests will fail.

The new isStrictlyStringContainingMatcher uses .includes('StringContaining') which returns true for both 'StringContaining' and 'StringNotContaining', silently converting negative matchers to positive ones during ignoreCase lowercasing.

src/utils.ts — isStrictlyStringContainingMatcher needs .includes('StringContaining') replaced with === 'StringContaining'

Important Files Changed

Filename Overview
src/utils.ts Adds Jasmine asymmetric matcher support with isAsymmetricMatcher, getAsymmetricMatcherValue, and new string-matcher helpers; isStrictlyStringContainingMatcher has a substring-check bug that breaks the ignoreCase path for negative (expect.not.stringContaining) matchers.
src/matchers/mock/toBeRequestedWith.ts Replaces local isMatcher with the shared isAsymmetricMatcher/getAsymmetricMatcherValue utilities, correctly supporting both Wdio (.sample) and Jasmine (.expected) matchers in requestedWithParamToString.
jasmine-wdio-expect-async.d.ts Rewrites the wdio/jasmine-framework type augmentation to expose all WDIO matchers as async, support withContext, and add Jasmine built-in sync matchers; introduces JasmineExpect interface and @ts-expect-error for the global expect override.
types/expect-webdriverio.d.ts Adds JasmineAsymmetricMatcher<R> type and AsymmetricMatcher<R> union alias; renames WdioMatchers to WdioCustomMatchers to decouple from Jest's expect-lib matchers, enabling Jasmine augmentation.
test/utils.test.ts Adds Jasmine-matcher test cases for compareText/compareTextWithArray and new utility function tests; pre-existing expect.not.stringContaining + ignoreCase tests will fail due to the isStrictlyStringContainingMatcher bug.
test/mocks/jasmine.ts New Jasmine StringContaining mock for unit tests; faithfully replicates Jasmine's runtime shape with .expected, asymmetricMatch, and jasmineToString.
types-checks-filter-out-node_modules.js Adds an error-filter entry to suppress the Expected<T> TS2304 that arises because @types/jasmine is excluded from tsconfig.types.json; workaround for a genuine type resolution gap.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["Asymmetric matcher passed to compareText/compareTextWithArray"] --> B{isAsymmetricMatcher?}
    B -- No --> C[Pass through unchanged]
    B -- Yes --> D{"isStringContainingMatcherLike? constructor.name === StringContaining"}
    D -- No --> E[Call asymmetricMatch directly]
    D -- Yes + ignoreCase --> F{isStrictlyStringContainingMatcher?}
    F -- "Wdio: toString().includes checks -- BUG matches StringNotContaining too" --> G["expect.stringContaining(sample)"]
    F -- "Jasmine: !isWdioMatcher always true" --> G
    F -- "Should be false for NOT matchers" --> H["expect.not.stringContaining(sample)"]
    G --> I[asymmetricMatch on lowercased actual]
    H --> I
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A["Asymmetric matcher passed to compareText/compareTextWithArray"] --> B{isAsymmetricMatcher?}
    B -- No --> C[Pass through unchanged]
    B -- Yes --> D{"isStringContainingMatcherLike? constructor.name === StringContaining"}
    D -- No --> E[Call asymmetricMatch directly]
    D -- Yes + ignoreCase --> F{isStrictlyStringContainingMatcher?}
    F -- "Wdio: toString().includes checks -- BUG matches StringNotContaining too" --> G["expect.stringContaining(sample)"]
    F -- "Jasmine: !isWdioMatcher always true" --> G
    F -- "Should be false for NOT matchers" --> H["expect.not.stringContaining(sample)"]
    G --> I[asymmetricMatch on lowercased actual]
    H --> I
Loading

Reviews (6): Last reviewed commit: "Update src/matchers/mock/toBeRequestedWi..." | Re-trigger Greptile

Comment on lines 31 to 33
// Check if the line matches any exclusion pattern
const shouldExclude = excludeList.some(excludePath =>
line.trim().startsWith(excludePath)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Silencing Expected<T> type error hides a genuine resolution gap

Expected<T> is a @types/jasmine utility type used in jasmine-wdio-expect-async.d.ts to type toBeResolvedTo. Because @types/jasmine is excluded from tsconfig.types.json, the type is unresolved and the error is suppressed here. Consumers who add expect-webdriverio/jasmine-wdio-expect-async to their types without @types/jasmine will see the same TS2304 in their own project. Substituting the concrete type directly would keep the type check clean without a filter entry.

@dprevost-LMI dprevost-LMI marked this pull request as draft June 9, 2026 00:16
@dprevost-LMI dprevost-LMI marked this pull request as ready for review June 21, 2026 13:20
Comment thread src/matchers/mock/toBeRequestedWith.ts Outdated
Comment thread src/matchers/mock/toBeRequestedWith.ts Outdated
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Comment thread src/utils.ts
Comment on lines +35 to +37
export function isStrictlyStringContainingMatcher(expected: unknown): expected is WdioAsymmetricMatcher<string> | JasmineAsymmetricMatcher<string> {
return isStringContainingMatcherLike(expected) && (!isWdioAsymmetricMatcher(expected) || expected.toString().includes('StringContaining'))
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 isStrictlyStringContainingMatcher incorrectly matches negative matchers

'StringNotContaining'.includes('StringContaining') is true because 'StringContaining' is a substring of 'StringNotContaining'. Both positive and negative Jest/expect matchers share the same StringContaining class (with an inverse flag), so constructor.name is identical for both. This means isStrictlyStringContainingMatcher(expect.not.stringContaining('FOO')) returns true instead of false.

The consequence: inside the ignoreCase branch of compareText and compareTextWithArray, a negative matcher like expect.not.stringContaining('FOO') is incorrectly lowercased and converted to expect.stringContaining('foo'), completely flipping the assertion. Pre-existing tests (compareText(' FOO ', expect.not.stringContaining('foo'), { ignoreCase: true }) expecting false) will now return true and fail.

The old code used expected.toString() === 'StringContaining' (strict equality), which correctly distinguished 'StringContaining' from 'StringNotContaining'. The fix is to restore that strict equality check for the Wdio-matcher branch.

Suggested change
export function isStrictlyStringContainingMatcher(expected: unknown): expected is WdioAsymmetricMatcher<string> | JasmineAsymmetricMatcher<string> {
return isStringContainingMatcherLike(expected) && (!isWdioAsymmetricMatcher(expected) || expected.toString().includes('StringContaining'))
}
export function isStrictlyStringContainingMatcher(expected: unknown): expected is WdioAsymmetricMatcher<string> | JasmineAsymmetricMatcher<string> {
return isStringContainingMatcherLike(expected) && (!isWdioAsymmetricMatcher(expected) || expected.toString() === 'StringContaining')
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Jasmine Augmentations Limitations & Future Jasmine withContext broken

2 participants