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

misc: convert lighthouse-core/test to ES modules #13295

Merged
merged 26 commits into from
May 3, 2022
Merged

Conversation

connorjclark
Copy link
Collaborator

@connorjclark connorjclark commented Nov 1, 2021

ref #12689

I suggest reviewing this PR one commit at a time. I converted entire folders at a time, ensuring that yarn jest continued to work throughout.

# These first two commits were very straightforward
core/test/config
core/test/audits
# Mocks were the main challenge here.
core/test/gather and core/test/fraggle-rock/gather
# Resolving the TS issues was a lot so I did that separately here
workaround the new jest unknown types
core/test/computed
core/test/lib
core/test/fraggle-rock
core/test
delete extra package.json s

Jest currently does not support the hoisting behavior of mocks in esmodules, meaning that test files using mocks must dynamically import some of their dependencies (just the ones that use files being mocked). I hope this is just a temporary workaround, so I left the regular imports as uncommented. If Jest ever supports this we'll be able to revert back to regular imports simply.

I didn't add a linter yet, can do so in later PR to reduce churn in this one.

@connorjclark connorjclark requested a review from a team as a code owner November 1, 2021 17:01
@connorjclark connorjclark requested review from adamraine and removed request for a team November 1, 2021 17:01
@google-cla google-cla bot added the cla: yes label Nov 1, 2021
@connorjclark connorjclark changed the title Merge remote-tracking branch 'origin/master' into esmodules-rollup core: convert lighthouse-core/test to ES modules Nov 1, 2021
@connorjclark connorjclark changed the title core: convert lighthouse-core/test to ES modules misc: convert lighthouse-core/test to ES modules Nov 1, 2021
@@ -1,6 +0,0 @@
printWidth: 100
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

👀

import PageDependencyGraph from '../../../computed/page-dependency-graph.js';
import LoadSimulator from '../../../computed/load-simulator.js';
import trace from '../../fixtures/traces/progressive-app-m60.json';
import devtoolsLog from '../../fixtures/traces/progressive-app-m60.devtools.log.json';
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I was surprised to see json imports working here. I guess Jest makes some allowances for it.

Copy link
Member

Choose a reason for hiding this comment

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

I was surprised to see json imports working here. I guess Jest makes some allowances for it.

how is this working? We have the jest transforms turned off and node should only do the import with --experimental-json-modules turned on (and even then only as import devtoolsLog from '../../fixtures/traces/progressive-app-m60.devtools.log.json' assert { type: 'json' };)

Copy link
Collaborator Author

@connorjclark connorjclark Nov 16, 2021

Choose a reason for hiding this comment

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

No idea! I was converting these to readJson, but once I noticed they still worked I stopped doing that midway and just allowed these to stay as json imports. I should probably finish the job...

Copy link
Collaborator Author

@connorjclark connorjclark Apr 19, 2022

Choose a reason for hiding this comment

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

wdyt: should all these become readJson or should we stick to importing them since that (somehow) works?

Copy link
Member

Choose a reason for hiding this comment

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

wdyt: should all these become readJson or should we stick to importing them since that (somehow) works?

Since the import is so simple I'm good with relying on whatever overly-magical thing jest is doing behind the scenes, with the hopes that by the time we need to do something different, import assertions will be stable in whatever minimum Node version we're using

emulationMock.clearThrottling = jest.fn();
emulationMock.emulate = jest.fn();
networkMock.fetchResponseBodyFromCache = jest.fn().mockResolvedValue('');
navigationMock.gotoURL = fnAny().mockResolvedValue({finalUrl: 'https://example.com', warnings: [], timedOut: false});
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

the jest here is now using types privided from jest directly via @jest/globals, whereas before it was from @types/jest (which is pinned to an older set of types). In the newer version, jest uses unknown where we relied on any for our types to work.

It's pretty awkward to use jsdoc to apply the correct template arguments, so I introduced a fnAny helper.

'assertNoSameOriginServiceWorkerClients'),
};
}

Copy link
Collaborator Author

@connorjclark connorjclark Nov 1, 2021

Choose a reason for hiding this comment

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

This is the most complex example of the dynamic import workaround required for mocks to work.

@@ -238,41 +177,49 @@ async function flushAllTimersAndMicrotasks(ms = 1000) {
* shouldn't concern themselves about.
*/
function makeMocksForGatherRunner() {
jest.mock('../gather/driver/environment.js', () => ({
jest.mock(require.resolve('../gather/driver/environment.js'), () => ({
getBenchmarkIndex: () => Promise.resolve(150),
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Even though this module is calling jest.mock, it seems that in esmodule test files the file is mocked relative to the test file instead. Using require.resolve to obtain an absolute path fixes that.

lighthouse-core/scripts/esm-utils.js Outdated Show resolved Hide resolved
lighthouse-core/gather/driver/navigation.js Outdated Show resolved Hide resolved
@connorjclark
Copy link
Collaborator Author

@adamraine @brendankenny want to look at this again?

Copy link
Member

@brendankenny brendankenny left a comment

Choose a reason for hiding this comment

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

ok, I'm like 80% through by file count, now only 80% of the complexity left :) Flushing comments...

const Audit = require('../../../audits/accessibility/aria-allowed-attr.js');
const assert = require('assert').strict;
import Audit from '../../../audits/accessibility/aria-allowed-attr.js';
import {strict as assert} from 'assert';
Copy link
Member

Choose a reason for hiding this comment

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

nit: do we want to start doing import assert from 'assert/strict';? Seems way better to me

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We use this current form in other places to, so let's handle it everywhere after this PR lands. tracking #13883

import PageDependencyGraph from '../../../computed/page-dependency-graph.js';
import LoadSimulator from '../../../computed/load-simulator.js';
import trace from '../../fixtures/traces/progressive-app-m60.json';
import devtoolsLog from '../../fixtures/traces/progressive-app-m60.devtools.log.json';
Copy link
Member

Choose a reason for hiding this comment

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

wdyt: should all these become readJson or should we stick to importing them since that (somehow) works?

Since the import is so simple I'm good with relying on whatever overly-magical thing jest is doing behind the scenes, with the hopes that by the time we need to do something different, import assertions will be stable in whatever minimum Node version we're using

const manifestWithoutMaskableSrc =
JSON.stringify(require('../fixtures/manifest-no-maskable-icon.json'));
JSON.stringify(readJson('lighthouse-core/test/fixtures/manifest-no-maskable-icon.json'));
Copy link
Member

Choose a reason for hiding this comment

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

should these change to the bare json imports too?

import manifest from 'lighthouse-core/test/fixtures/manifest.json';
import manifestWithoutMaskable = 'lighthouse-core/test/fixtures/manifest-no-maskable-icon.json';

const manifestSrc = JSON.stringify(manifest);
const manifestWithoutMaskableSrc = JSON.stringify(manifestWithoutMaskable);

lighthouse-core/test/audits/splash-screen-test.js Outdated Show resolved Hide resolved
lighthouse-core/test/audits/themed-omnibox-test.js Outdated Show resolved Hide resolved
const BaseGatherer = require('../../../fraggle-rock/gather/base-gatherer.js');
const {initializeConfig} = require('../../../fraggle-rock/config/config.js');
const {LH_ROOT} = require('../../../../root.js');
import {jest} from '@jest/globals';
Copy link
Member

Choose a reason for hiding this comment

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

this isn't injected but describe, it, etc is?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

correct.

@@ -0,0 +1,4 @@
{
"type": "commonjs",
"//": "jest only supports commonjs for setup modules"
Copy link
Member

Choose a reason for hiding this comment

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

🙍

@connorjclark
Copy link
Collaborator Author

@brendankenny did you have more review to share?

Copy link
Member

@brendankenny brendankenny left a comment

Choose a reason for hiding this comment

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

Sorry I was the blocker on this. Changes look good to me, but I only looked at a high level at the mocking changes and I'm hoping @adamraine looked closer than I did :)

Yay big progress on esm!

/**
* @param {ImportMeta} importMeta
*/
function createCommonjsRefs(importMeta) {
Copy link
Member

Choose a reason for hiding this comment

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

I can see the benefit of reducing churn in this change by providing this, but it does seem like we should be looking to the future, too. e.g. new files that need a _dirname + '/path/to/something', should really be doing new URL('/path/to/something', import.meta.url) rather than encouraging further use of __filename and _dirname

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

agreed-#13953

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.

4 participants