Skip to content

Commit

Permalink
Merge pull request dilanx#509 from stevenewald:main
Browse files Browse the repository at this point in the history
Creation of jest testing framework, along with basic integration/unit tests
  • Loading branch information
dilanx authored May 2, 2023
2 parents 70a7a2a + 10f3149 commit 42c756c
Show file tree
Hide file tree
Showing 37 changed files with 5,292 additions and 1,649 deletions.
6,123 changes: 4,474 additions & 1,649 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
"packages/*"
],
"scripts": {
"test:unit": "jest --config test/unit/jest.config.js",
"test:integration": "jest --config test/integration/jest.config.js --runInBand",
"lint": "npm run lint:ts && npm run lint:es",
"lint:ts": "tsc --noEmit",
"lint:es": "eslint --ext .ts",
Expand All @@ -31,7 +33,10 @@
"@typescript-eslint/eslint-plugin": "^5.38.1",
"@typescript-eslint/parser": "^5.38.1",
"eslint": "^8.24.0",
"jest": "^29.5.0",
"jest-playwright-preset": "^3.0.1",
"lerna": "^6.5.1",
"playwright": "^1.33.0",
"prettier": "2.7.1"
}
}
39 changes: 39 additions & 0 deletions test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# CRACO End-to-End Tests

## Usage

These tests ensure various functionality contracts are upheld across dependency upgrades.

To get started locally, run `npm run test:unit` or `npm run test:integration`.

## How do these work?

### `unit/`

These tests are non-integration and do not involve the building of any individual packages.

### `integration/fixtures/`

Each `fixture/` gets spun up in a temporary directory and has its dependencies installed with npm.<br>

### `integration/setup.js`

This script runs before any integration tests are executed. It creates a temporary directory for each fixture, installs the local version of CRACO, any other required packages, and builds the package. You can then use an individual <test>.test.js within the fixture to start a server or check for properties of the build files.

### `integration/teardown.js`

This removes all temporary directories generated during integration tests.

## How can I make my own tests?

### Unit tests

To create your own unit test, you can simply setup a directory within `unit/`, make a <test>.test.js, import necessary craco files, then test specific features. This test can be run with `npm run test:unit`.

### Integration tests

To create your own integration test, create a new directory under `integration/fixtures/`. You can then create a directory within called `test-package-files`, which should contain all necessary files for your package (public/index.html, craco.config.js, src/index.js, etc). These will be copied over to the temporary directory on test execution.

You can then create a index.test.js in `integration/fixtures/<testname>` to interface with the built package. It's recommended run a local server, then use playwright to test individual features of the live website. View `integration/fixtures/basic-integration-test` for an example.

This integration test will be run with `npm run test:integration`.
38 changes: 38 additions & 0 deletions test/integration/fixtures/basic-integration-test/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
'use strict';
const { join } = require('path');
const { execSync, spawn } = require('child_process');

beforeAll(async () => {
// Start a local server to serve the test project
const server = spawn('npx', ['serve', '-s', 'build', '-l', global.PORT], {
cwd: join(__dirname, 'test-project'),
});

// Log any server errors to the console
server.stderr.on('data', (data) => {
console.error(`stderr: ${data}`);
});

// Leave time for the server to initialize
await new Promise((resolve) => {
setTimeout(resolve, 3000);
});
});

test('Should have the expected styles', async () => {
await page.goto(global.URL, { waitUntil: 'domcontentloaded' }); //todo: make the url changeble

const cracoTestText = await page.$eval(
'#craco-test',
(element) => element.textContent
);
expect(cracoTestText).toBe('CRACO is working!');
});

afterAll(() => {
// Stop the local server
execSync(`kill $(lsof -t -i:${global.PORT})`, {
cwd: __dirname,
stdio: 'ignore',
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
const webpack = require('webpack');
const isDevelopment = false;
module.exports = {
webpack: {
configure: (webpackConfig) => {
if (!isDevelopment) {
const reactRefreshPluginIndex = webpackConfig.plugins.findIndex(
(plugin) => plugin.constructor.name === 'ReactRefreshPlugin'
);

if (reactRefreshPluginIndex !== -1) {
webpackConfig.plugins.splice(reactRefreshPluginIndex, 1);
}

const babelLoader = webpackConfig.module.rules
.find(
(rule) =>
rule.oneOf &&
rule.oneOf.find(
(r) => r.loader && r.loader.includes('babel-loader')
)
)
.oneOf.find((r) => r.loader && r.loader.includes('babel-loader'));

const reactRefreshBabelIndex = babelLoader.options.plugins.findIndex(
(plugin) =>
plugin && plugin.includes && plugin.includes('react-refresh/babel')
);

if (reactRefreshBabelIndex !== -1) {
babelLoader.options.plugins.splice(reactRefreshBabelIndex, 1);
}
}

webpackConfig.plugins.push(
new webpack.DefinePlugin({
__CUSTOM_GLOBAL_CONSTANT__: JSON.stringify('CRACO is working!'),
})
);

return webpackConfig;
},
},
};

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

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "craco-basic-test",
"version": "0.1.0",
"private": true,
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"web-vitals": "^3.3.1"
},
"scripts": {
"start": "craco start",
"build": "craco build",
"test": "craco test",
"eject": "react-scripts eject"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>React App</title>
<link rel="stylesheet" href="index.css" />
</head>
<body>
<div id="root"></div>
<script src="index.js"></script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/* global __CUSTOM_GLOBAL_CONSTANT__ */
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';

ReactDOM.render(
<React.StrictMode>
<div className="container">
<h1>Testing CRACO</h1>
<p id="craco-test">{__CUSTOM_GLOBAL_CONSTANT__}</p>
</div>
</React.StrictMode>,
document.getElementById('root')
);
28 changes: 28 additions & 0 deletions test/integration/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use strict';

module.exports = {
// testEnvironment: 'node',
testMatch: ['<rootDir>/**/*.test.js'],
testPathIgnorePatterns: ['/src/', 'node_modules'],
transform: {
'^.+\\.jsx?$': 'babel-jest',
},
moduleNameMapper: {
'\\.(css|less|scss|sass)$': 'identity-obj-proxy',
},
setupFiles: ['./setup.js'],
globalTeardown: './teardown.js',
testEnvironmentOptions: {
'jest-playwright': {
browsers: ['firefox'],
launchOptions: {
headless: true,
},
},
},
globals: {
PORT: 3009,
URL: 'http://localhost:3009',
},
preset: 'jest-playwright-preset',
};
48 changes: 48 additions & 0 deletions test/integration/setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
const { join } = require('path');
const { execSync } = require('child_process');
const fs = require('fs');
const rootPath = 'test/integration/fixtures';

const cwd = process.cwd();

// Set up the environment for integration tests
module.exports = async function (globalConfig, projectConfig) {
fs.readdir(rootPath, { withFileTypes: true }, (err, entries) => {
if (err) {
console.error(`Error reading directory: ${err.message}`);
return;
}

const directoryNames = entries
.filter((entry) => entry.isDirectory())
.map((entry) => entry.name);

directoryNames.forEach((directoryName) => {
//copy files in directory/test-package-files to directory/test-project
const testPackageFilesPath = join(
rootPath,
directoryName,
'test-package-files'
);
const testProjectPath = join(rootPath, directoryName, 'test-project');
execSync(
`cd ${join(
rootPath,
directoryName
)}&& npx create-react-app test-project`,
{ cwd: cwd }
);
execSync(`cp -r ${testPackageFilesPath}/* ${testProjectPath}`, {
cwd: cwd,
});
//install craco
execSync(`npm install ../../../../../packages/craco`, {
cwd: testProjectPath,
});
//install other necessary files
execSync(`npm install`, { cwd: testProjectPath });
//build project
execSync('npm run build', { cwd: testProjectPath, stdio: 'inherit' });
});
});
};
28 changes: 28 additions & 0 deletions test/integration/teardown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Clean up the environment after integration tests
const { execSync } = require('child_process');
const { join } = require('path');
const fs = require('fs');

const rootPath = 'test/integration/fixtures';

const cwd = process.cwd();

module.exports = async () => {
fs.readdir(rootPath, { withFileTypes: true }, (err, entries) => {
if (err) {
console.error(`Error reading directory: ${err.message}`);
return;
}

const directoryNames = entries
.filter((entry) => entry.isDirectory())
.map((entry) => entry.name);

directoryNames.forEach((directoryName) => {
//clean up test-project
execSync(`rm -rf ${join(rootPath, directoryName, 'test-project')}`, {
cwd: cwd,
});
});
});
};
13 changes: 13 additions & 0 deletions test/unit/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
'use strict';

module.exports = {
testEnvironment: 'node',
testMatch: ['<rootDir>/**/*.test.js'],
testPathIgnorePatterns: ['/src/', 'node_modules'],
transform: {
'^.+\\.jsx?$': 'babel-jest',
},
moduleNameMapper: {
'\\.(css|less|scss|sass)$': 'identity-obj-proxy',
},
};
Loading

0 comments on commit 42c756c

Please sign in to comment.