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

chore(integration-karma): migrate to playwright/vitest #4594

Open
wants to merge 122 commits into
base: master
Choose a base branch
from

Conversation

cardoso
Copy link
Contributor

@cardoso cardoso commented Oct 1, 2024

Details

I'm pushing this PR as draft to better manage the diff and eventually clean it up for review.

It adds a yarn test:browser script to @lwc/integration-karma which runs a vitest config in browser mode.

The vitest config uses plugins and setup scripts that mimic the custom scripts and plugins used by karma, but are being written mostly from scratch.

The aim during migration is to ideally have minimal modification to tests, so the setup scripts polyfills jasmine and other karma globals. There's some exceptions: Some tests add custom matchers. I prefer to move those to the setup scripts, since polyfilling jasmine.addMatchers with expect.extend is more work than it's worth.

yarn test (karma) runs unmodified.
yarn test:browser (playwright/vitest) currently passes almost all tests but rewriting the plugins, scripts, and polyfills is still ongoing.

Once yarn test:browser is passing all tests, the plan is to remove karma completely, and transition this PR to ready for review. Most likely I'll leave the CI and coverage work for reviewers.

Same for yarn hydration:test (karma) and yarn hydration:browser (playwright/vitest)

If this works out, future tests can be written using facilities from vitest browser mode and playwright.

Old tests can be modernized as needed, and eventually the jasmine/karma polyfills can be removed.

Closes #3589

Current status

test-hydration (all)

 Test Files  136 passed (136)
      Tests  136 passed (136)
   Start at  21:54:55
   Duration  4.30s (transform 0ms, setup 3.76s, collect 5.58s, tests 263ms, environment 0ms, prepare 1.18s)

test (firefox)

 Test Files  314 passed (314)
      Tests  3213 passed | 12 skipped (3225)
   Start at  21:55:37
   Duration  31.14s (transform 0ms, setup 30.82s, collect 16.13s, tests 5.21s, environment 0ms, prepare 7.92s)

test (chromium)

 Test Files  1 failed | 313 passed (314)
      Tests  1 failed | 3212 passed | 12 skipped (3225)
   Start at  21:53:26
   Duration  11.09s (transform 0ms, setup 9.61s, collect 17.15s, tests 2.64s, environment 0ms, prepare 2.88s)

Does this pull request introduce a breaking change?

  • 😮‍💨 No, it does not introduce a breaking change.

Does this pull request introduce an observable change?

  • 🤞 No, it does not introduce an observable change.

GUS work item

@cardoso cardoso requested a review from a team as a code owner October 1, 2024 12:38
@cardoso cardoso changed the title chore(integration-karma): migrate from karma/jasmine to playwright/vitest chore(integration-karma): migrate to playwright/vitest Oct 1, 2024
@cardoso cardoso marked this pull request as draft October 1, 2024 12:39
Copy link
Contributor Author

@cardoso cardoso left a comment

Choose a reason for hiding this comment

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

I'll do a clean up before continuing the migration work, but here's some notes on what's going on.

Comment on lines 8 to 11
window.HydrateTest = (function (lwc, testUtils) {
testUtils.setHooks({
sanitizeHtmlContent: (content) => content,
});
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I haven't started migrating test-hydrate yet, although I'm keeping it in mind while rewriting the scripts and plugins.

Comment on lines 8 to 13
'use strict';

import { resolve } from 'path';

import options from '../../shared/options';
const { ENABLE_SYNTHETIC_SHADOW_IN_HYDRATION, GREP, COVERAGE, COVERAGE_DIR_FOR_OPTIONS } = options;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Some scripts are just copied, ported to typescript, and kept for reference to rewrite / refactor. They aren't really used as it's too karma-specific.

Comment on lines 7 to 15
import path from 'node:path';
import chokidar, { FSWatcher } from 'chokidar';

export default class Watcher {
_suiteDependencyLookup: {
[x: string]: any;
};
_watcher: FSWatcher;
constructor(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think this will be used as vitest handles it out of the box and has customization options.

Comment on lines 192 to 196
createHCONFIG2JSPreprocessor.$inject = ['config', 'logger', 'emitter'];

export default {
'preprocessor:hydration-tests': ['factory', createHCONFIG2JSPreprocessor],
};
Copy link
Contributor Author

Choose a reason for hiding this comment

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

hydration-tests plugin not yet rewritten.

Copy link
Contributor Author

@cardoso cardoso left a comment

Choose a reason for hiding this comment

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

I made some changes the @lwc/rollup-plugin to simplify this migration. They could be removed before merge, or kept if there's some perf benefit in addition to simplifying the vitest plugin.

Comment on lines 158 to 164
let {
rootDir,
modules = [], // TODO [#3370]: remove experimental template expression flag
experimentalComplexExpressions,
apiVersion,
} = pluginOptions;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Making some options configurable after the fact

Comment on lines 362 to 385
api: {
updateOptions(options: {
rootDir: string;
modules?: ModuleRecord[];
experimentalComplexExpressions?: boolean;
apiVersion?: number;
}) {
rootDir = options.rootDir;

modules = [
...(options.modules ?? []),
...DEFAULT_MODULES,
{ dir: options.rootDir },
];

if (options.experimentalComplexExpressions !== undefined) {
experimentalComplexExpressions = options.experimentalComplexExpressions;
}

if (options.apiVersion !== undefined) {
apiVersion = options.apiVersion;
}
},
},
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Providing an api to change some options to avoid recreating the plugin for tests and fixtures which require different settings.

I've since found a cleaner approach which I might use as reference: https:/vitejs/vite-plugin-vue/blob/main/packages/plugin-vue/src/index.ts#L192-L200

Comment on lines -20 to -42
beforeAll(function () {
const getNormalizedFunctionAsString = (fn) => fn.toString().replace(/(\s|\n)/g, '');

jasmine.addMatchers({
toEqualWireSettings: function () {
return {
compare: function (actual, expected) {
Object.keys(actual).forEach((currentKey) => {
const normalizedActual = Object.assign({}, actual[currentKey], {
config: getNormalizedFunctionAsString(actual[currentKey].config),
});

const normalizedExpected = Object.assign({}, expected[currentKey], {
config: getNormalizedFunctionAsString(
expected[currentKey].config || function () {}
),
});

expect(normalizedActual).toEqual(normalizedExpected);
});

return {
pass: true,
Copy link
Contributor Author

@cardoso cardoso Oct 1, 2024

Choose a reason for hiding this comment

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

This beforeAll() was just adding a custom matcher. I moved it to the test scripts in karma and implemented the equivalent in the vitest scripts.

@nolanlawson
Copy link
Collaborator

Thanks a lot for this PR! I haven't had a chance to review yet, but I have some quick thoughts:

  1. We definitely need to get off of Karma, since it is deprecated
  2. However, I'm concerned about using Vite, because one of the nice things about our current Karma+Jasmine setup is that all tests run in a single browser tab and single tab session. This means the tests run really fast, at the cost of forcing us to do manual cleanup between each test run. (Although this manual cleanup has other benefits – some tests actually confirm that the LWC engine is properly cleaning up state between tests, e.g. this test would fail if our cache keys were overbroad.)
  3. I'm also concerned about using Playwright, since 1) AIUI it does not allow us to test multiple browser versions, and 2) it tests WebKit rather than "real" Safari. There are differences between WebKit and Safari and I'd rather we not delude ourselves by testing a browser that no real user is using.
  4. Moving off of Jasmine also has some issues which you've highlighted, i.e. that you have to mock some Jasmine behavior. We have a lot of tests that hook into Jasmine's internals in subtle ways (e.g. here) and this could be tricky to mock.

Originally, I had planned on us migrating to something like Web Test Runner with Jasmine. That's not to say Vite isn't the right choice (we're already using it elsewhere), but that is what I had in mind.

@cardoso
Copy link
Contributor Author

cardoso commented Oct 16, 2024

@nolanlawson thanks for looking into it!

re 2: Vitest also runs in a single tab and the test speed hasn't changed much (although hydration tests runs 2x faster with a tweak I made). I kept the clean up routine the same and mostly ported the ad-hoc plugins without much behavioral change.

re 3: we can definitely test different browser versions with playwright using providerOptions, but also we can use webdriverio and its own providerOptions. Admittedly, I haven't looked into any of this yet as my current focus is making all the different flags work (as well as the single failing test in chrome).

re 4: I'm not sure that case is currently causing any issues, at least not with the current flags, but I get your point.

There's a bunch of work I'm not sure how to handle yet, especially coverage and merging the results, because I want to first figure out all the flags and especially this failing test (only on chrome/webkit and maybe only on macOS. Firefox works fine.):

test/shadow-dom/ShadowRoot.elementsFromPoint

There's no rush on this PR (it can be left in draft and closed at some point), but I'd love your feedback especially regarding that failing test.

My current plan is to make some smaller PRs based on my learnings that would help transition into this setup or another setup.

@nolanlawson
Copy link
Collaborator

elementsFromPoint is a tricky one because the browsers disagree about how it should be implemented. (Browser folks have a lot of jokes about "the hit-testing spec" – there isn't one 🤣.) If it's failing, it could be due to some subtlety that isn't worth fixing in the test. The goal is just to make sure that our shadow DOM polyfill gives a result that is consistent with browsers.

also we can use webdriverio and its own providerOptions

I would much prefer this. WebDriver is the standard, we are already using webdriverio elsewhere, and I imagine this would play nicer with our current SauceLabs setup. The way that Playwright hacks around with CDP and a custom WebKit build makes me nervous.

Vitest also runs in a single tab and the test speed hasn't changed much (although hydration tests runs 2x faster with a tweak I made)

This is interesting, I wasn't aware of this... I assumed Vitest was doing some kind of test isolation, i.e. with browser tabs. If the tests take the same-ish amount of time to run, then my concern goes away. The current lack of test isolation isn't a hard requirement either.

Overall I really appreciate your work on this, but yeah this PR might end up being delayed as we work on other stuff. Right now it's kind of a crunch time for my team, so we may not be able to give this PR as much attention as it deserves. I really appreciate your help, though! Let me know if there's anything else I can answer to help unblock you. 🙂

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

Successfully merging this pull request may close these issues.

Replace karma because it is deprecated
3 participants