Skip to content

Commit

Permalink
Add an integration test that builds the examples with release artifacts
Browse files Browse the repository at this point in the history
  • Loading branch information
etrepum committed Apr 21, 2024
1 parent bbaeb49 commit 649c068
Show file tree
Hide file tree
Showing 13 changed files with 254 additions and 7 deletions.
7 changes: 4 additions & 3 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,8 @@ module.exports = {

'react/jsx-tag-spacing': ERROR,

'react/jsx-uses-react': ERROR,
// This hasn't been necessary since React 17
'react/jsx-uses-react': OFF,

// We don't care to do this
'react/jsx-wrap-multilines': [
Expand All @@ -225,8 +226,8 @@ module.exports = {

'react/no-is-mounted': OFF,

// This isn't useful in our test code
'react/react-in-jsx-scope': ERROR,
// This hasn't been necessary since React 17
'react/react-in-jsx-scope': OFF,

'react/self-closing-comp': ERROR,

Expand Down
27 changes: 27 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,33 @@ jobs:
run: npm ci
- run: npm run test-unit

integration:
if: github.repository_owner == 'facebook'
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.18.0]
env:
CI: true
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- run: npm i -g npm@8
- uses: actions/cache@v3
id: cache
with:
path: |
node_modules
~/.cache/ms-playwright
key: ${{ runner.os }}-v${{ secrets.CACHE_VERSION }}-${{ hashFiles('package-lock.json') }}
- name: Install dependencies
if: steps.cache.outputs.cache-hit != 'true'
run: npm ci
- run: npm run test-integration

e2e-mac:
if: github.repository_owner == 'facebook'
runs-on: macos-latest
Expand Down
1 change: 0 additions & 1 deletion examples/react-rich/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {ContentEditable} from '@lexical/react/LexicalContentEditable';
import LexicalErrorBoundary from '@lexical/react/LexicalErrorBoundary';
import {HistoryPlugin} from '@lexical/react/LexicalHistoryPlugin';
import {RichTextPlugin} from '@lexical/react/LexicalRichTextPlugin';
import * as React from 'react';

import ExampleTheme from './ExampleTheme';
import ToolbarPlugin from './plugins/ToolbarPlugin';
Expand Down
3 changes: 1 addition & 2 deletions examples/react-rich/src/plugins/ToolbarPlugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import {
UNDO_COMMAND,
} from 'lexical';
import {useCallback, useEffect, useRef, useState} from 'react';
import * as React from 'react';

const LowPriority = 1;

Expand Down Expand Up @@ -57,7 +56,7 @@ export default function ToolbarPlugin() {
}),
editor.registerCommand(
SELECTION_CHANGE_COMMAND,
(_payload, newEditor) => {
(_payload, _newEditor) => {
$updateToolbar();
return false;
},
Expand Down
1 change: 0 additions & 1 deletion examples/react-rich/src/plugins/TreeViewPlugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
import {TreeView} from '@lexical/react/LexicalTreeView';
import * as React from 'react';

export default function TreeViewPlugin(): JSX.Element {
const [editor] = useLexicalComposerContext();
Expand Down
6 changes: 6 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ module.exports = {
],
},
},
{
...common,
displayName: 'integration',
globalSetup: './scripts/__tests__/integration/setup.js',
testMatch: ['**/scripts/__tests__/integration/**/*.test.js'],
},
{
...common,
displayName: 'e2e',
Expand Down
19 changes: 19 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"validation": "npx ts-node --cwdMode packages/lexical-playground/src/server/validation.ts",
"test-unit": "jest --selectProjects unit --testPathPattern",
"test-unit-watch": "npm run test-unit -- --watch --coverage=''",
"test-integration": "jest --selectProjects integration --testPathPattern",
"test-e2e-chromium": "cross-env E2E_BROWSER=chromium playwright test --project=\"chromium\"",
"test-e2e-firefox": "cross-env E2E_BROWSER=firefox playwright test --project=\"firefox\"",
"test-e2e-webkit": "cross-env E2E_BROWSER=webkit playwright test --project=\"webkit\"",
Expand Down Expand Up @@ -121,6 +122,7 @@
"@rollup/plugin-replace": "^3.0.0",
"@rollup/plugin-terser": "^0.4.4",
"@size-limit/preset-big-lib": "^11.1.2",
"@types/child-process-promise": "^2.2.6",
"@types/jest": "^29.4.0",
"@types/jsdom": "^21.1.6",
"@types/katex": "^0.16.7",
Expand Down
32 changes: 32 additions & 0 deletions scripts/__tests__/integration/prepare-release.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
// @ts-check
'use strict';

const fs = require('fs-extra');
const glob = require('glob');
const {packagesManager} = require('../../shared/packagesManager');
const path = require('node:path');
const {describeExample} = require('./utils');

describe('prepare-release tests', () => {
for (const pkg of packagesManager.getPublicPackages()) {
describe(pkg.getNpmName(), () => {
const tgzPath = path.join(
'npm',
`${pkg.getDirectoryName()}-${pkg.packageJson.version}.tgz`,
);
test(`${tgzPath} exists`, () =>
expect(fs.existsSync(tgzPath)).toBe(true));
});
}
test('builds', async () => {});
});
glob
.sync('examples/*/package.json')
.forEach((exampleJsonPath) => describeExample(exampleJsonPath, () => {}));
45 changes: 45 additions & 0 deletions scripts/__tests__/integration/setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
// @ts-check
'use strict';

const path = require('node:path');
const fs = require('fs-extra');
const {exec} = require('child-process-promise');
const {packagesManager} = require('../../shared/packagesManager');
const {version} = require('../../shared/readMonorepoPackageJson')();

/**
* @param {import('@jest/types').Config.GlobalConfig} globalConfig
* @param {import('@jest/types').Config.ProjectConfig} projectConfig
*/
module.exports = async function (globalConfig, projectConfig) {
const needsBuild = packagesManager
.getPublicPackages()
.some(
(pkg) =>
!fs.existsSync(
path.resolve(`./npm/${pkg.getDirectoryName()}-${version}.tgz`),
),
);
if (!needsBuild) {
return;
}
await exec('npm run prepare-release');
const packDest = path.resolve('./npm');
fs.mkdirpSync(packDest);
for (const pkg of packagesManager.getPublicPackages()) {
const cwd = process.cwd();
try {
process.chdir(pkg.resolve('npm'));
await exec(`npm pack --pack-destination '${packDest}'`);
} finally {
process.chdir(cwd);
}
}
};
116 changes: 116 additions & 0 deletions scripts/__tests__/integration/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
// @ts-check
'use strict';

const {exec} = require('child-process-promise');
const {packagesManager} = require('../../shared/packagesManager');
const fs = require('fs-extra');
const path = require('node:path');

const LONG_TIMEOUT = 60 * 1000;

/**
* exec the command in a child process from the given directory.
*
* @param {string} dir
* @param {Parameters<typeof exec>} args
* @returns {Promise<string>}
*/
async function dirExec(dir, ...args) {
const cwd = process.cwd();
try {
process.chdir(dir);
return await exec(...args);
} finally {
process.chdir(cwd);
}
}

exports.dirExec = dirExec;

/**
* @typedef {Object} ExampleContext
* @property {string} packageJsonPath
* @property {string} exampleDir
* @property {Record<string, any>} packageJson
*/

/** @param {ctx} ExampleContext */
async function buildExample({packageJson, exampleDir}) {
const lexicalPackages = new Map(
packagesManager.getPublicPackages().map((pkg) => [pkg.getNpmName(), pkg]),
);
const installDeps = [
'dependencies',
'devDependencies',
'peerDependencies',
].flatMap((depType) => {
const deps = packageJson[depType] || {};
return Object.keys(deps).flatMap((k) => {
const pkg = lexicalPackages.get(k);
return pkg
? [
path.resolve(
'npm',
`${pkg.getDirectoryName()}-${pkg.packageJson.version}.tgz`,
),
]
: [];
});
});
if (installDeps.length === 0) {
throw new Error(`No lexical dependencies detected: ${exampleDir}`);
}
['node_modules', 'dist', 'build'].forEach((cleanDir) =>
fs.removeSync(path.resolve(exampleDir, cleanDir)),
);
await dirExec(
exampleDir,
`npm install --no-save --no-package-lock ${installDeps
.map((fn) => `'${fn}'`)
.join(' ')}`,
);
await dirExec(exampleDir, `npm run build`);
}

/**
* Build the example project with prerelease lexical artifacts
*
* @param {string} packgeJsonPath
* @param {(ctx: ExampleContext) => void} bodyFun
*/
function describeExample(packageJsonPath, bodyFun) {
const packageJson = fs.readJsonSync(packageJsonPath);
const exampleDir = path.dirname(packageJsonPath);
/** @type {ExampleContext} */
const ctx = {exampleDir, packageJson, packageJsonPath};
describe(exampleDir, () => {
beforeAll(async () => buildExample(ctx), LONG_TIMEOUT);
test('install & build succeeded', () => {
expect(true).toBe(true);
});
test('installed lexical', () => {
expect(
fs.existsSync(path.join(exampleDir, 'node_modules', 'lexical')),
).toBe(true);
});
if (packageJson.scripts.test) {
test(
'tests pass',
async () => {
expect(await dirExec(exampleDir, 'npm run build')).not.toBe(null);
},
LONG_TIMEOUT,
);
}
bodyFun(ctx);
});
}

exports.describeExample = describeExample;
1 change: 1 addition & 0 deletions scripts/__tests__/unit/build.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* LICENSE file in the root directory of this source tree.
*
*/
// @ts-check
'use strict';

const fs = require('fs-extra');
Expand Down
1 change: 1 addition & 0 deletions scripts/clean.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const fs = require('fs-extra');
const path = require('node:path');
const {packagesManager} = require('./shared/packagesManager');

fs.removeSync(path.resolve(`./npm`));
fs.removeSync(path.resolve(`./.ts-temp`));
packagesManager
.getPublicPackages()
Expand Down

0 comments on commit 649c068

Please sign in to comment.