Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ module.exports = {
// snapshotSerializers: [],

// The test environment that will be used for testing
testEnvironment: 'jsdom'
testEnvironment: 'jsdom',

// Options that will be passed to the testEnvironment
// testEnvironmentOptions: {},
Expand All @@ -165,15 +165,15 @@ module.exports = {
// testLocationInResults: false,

// The glob patterns Jest uses to detect test files
// testMatch: [
// "**/__tests__/**/*.[jt]s?(x)",
// "**/?(*.)+(spec|test).[tj]s?(x)"
// ],
testMatch: [
"**/?(*.)+(test).[tj]s?(x)"
],

// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
// testPathIgnorePatterns: [
// "/node_modules/"
// ],
testPathIgnorePatterns: [
"/node_modules/",
"\\.ssr\\.test\\.[tj]sx?$"
]

// The regexp pattern or array of patterns that Jest uses to detect test files
// testRegex: [],
Expand Down
43 changes: 43 additions & 0 deletions jest.ssr.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright 2020 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

// For a detailed explanation regarding each configuration property, visit:
// https://jestjs.io/docs/en/configuration.html

module.exports = {
// Automatically clear mock calls and instances between every test
clearMocks: true,
displayName: 'SSR',

// A map from regular expressions to module names that allow to stub out resources with a single module
moduleNameMapper: {
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '<rootDir>/__mocks__/fileMock.js',
'\\.(css|styl)$': 'identity-obj-proxy',
'\\.\./Icon/.*$': '<rootDir>/__mocks__/iconMock.js'
},

// Run tests from one or more projects
projects: ['<rootDir>'],

// A list of paths to directories that Jest should use to search for files in
roots: [
'packages/'
],

// The test environment that will be used for testing
testEnvironment: require.resolve('@react-spectrum/test-utils/src/SSREnvironment'),

// The glob patterns Jest uses to detect test files
testMatch: [
"**/?(*.)+(ssr.test).[tj]s?(x)"
]
};
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"start:docs": "PARCEL_WORKER_BACKEND=process DOCS_ENV=dev parcel 'packages/@react-{spectrum,aria,stately}/*/docs/*.mdx' 'packages/dev/docs/pages/**/*.mdx'",
"build:docs": "PARCEL_WORKER_BACKEND=process DOCS_ENV=staging parcel build 'packages/@react-{spectrum,aria,stately}/*/docs/*.mdx' 'packages/dev/docs/pages/**/*.mdx' --no-scope-hoist",
"test": "yarn jest",
"test:ssr": "yarn jest --config jest.ssr.config.js",
"ci-test": "yarn jest --maxWorkers=2",
"lint": "yarn check-types && eslint packages --ext .js,.ts,.tsx && node scripts/lint-packages.js",
"jest": "node scripts/jest.js",
Expand Down
28 changes: 28 additions & 0 deletions packages/@react-spectrum/button/test/Button.ssr.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2020 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

import {testSSR} from '@react-spectrum/test-utils';

describe('Button SSR', function () {
it.each`
Name | props
${'ActionButton'} | ${{}}
${'Button'} | ${{}}
${'FieldButton'} | ${{}}
${'LogicButton'} | ${{}}
`('$Name should render without errors', async function ({Name, props}) {
await testSSR(__filename, `
import {${Name}} from '../';
<${Name}>Button</${Name}>
`);
});
});
22 changes: 22 additions & 0 deletions packages/@react-spectrum/checkbox/test/Checkbox.ssr.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright 2020 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

import {testSSR} from '@react-spectrum/test-utils';

describe('Checkbox SSR', function () {
it('should render without errors', async function () {
await testSSR(__filename, `
import {Checkbox} from '../';
<Checkbox>Test</Checkbox>
`);
});
});
27 changes: 27 additions & 0 deletions packages/@react-spectrum/dialog/test/Dialog.ssr.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright 2020 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

import {testSSR} from '@react-spectrum/test-utils';

describe('Dialog SSR', function () {
it('Dialog should render without errors', async function () {
await testSSR(__filename, `
import {Dialog} from '../';

<Dialog>
contents
</Dialog>
`);
});

// TODO: AlertDialog, DialogTrigger
});
22 changes: 22 additions & 0 deletions packages/@react-spectrum/divider/test/Divider.ssr.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright 2020 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

import {testSSR} from '@react-spectrum/test-utils';

describe('Divider SSR', function () {
it('should render without errors', async function () {
await testSSR(__filename, `
import {Divider} from '../';
<Divider />
`);
});
});
27 changes: 27 additions & 0 deletions packages/@react-spectrum/form/test/Form.ssr.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright 2020 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

import {testSSR} from '@react-spectrum/test-utils';

describe('Form SSR', function () {
it('should render without errors', async function () {
await testSSR(__filename, `
import {Provider} from '@react-spectrum/provider';
import {theme} from '@react-spectrum/theme-default';
import {Form} from '../';

<Provider theme={theme}>
<Form aria-label="Home" />
</Provider>
`);
});
});
6 changes: 4 additions & 2 deletions packages/@react-spectrum/test-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,13 @@
"url": "https://github.com/adobe/react-spectrum"
},
"dependencies": {
"@babel/runtime": "^7.6.2"
"@babel/runtime": "^7.6.2",
"resolve": "^1.17.0"
},
"peerDependencies": {
"@testing-library/react": "^8.0.1",
"react": "^16.8.0"
"react": "^16.8.0",
"react-dom": "^16.8.0"
},
"devDependencies": {
"@adobe/spectrum-css-temp": "^3.0.0-alpha.1"
Expand Down
31 changes: 31 additions & 0 deletions packages/@react-spectrum/test-utils/src/SSREnvironment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright 2020 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

const NodeEnvironment = require('jest-environment-node');
const {Worker} = require('worker_threads');

// Setup a single worker instance that's shared between test environments via a __SSR_WORKER__ global.
let worker = new Worker(__dirname + '/hydrateWorker.js');
worker.unref();

class SSREnvironment extends NodeEnvironment {
async setup() {
await super.setup();
this.global.__SSR_WORKER__ = worker;
}

async teardown() {
await super.teardown();
}
}

module.exports = SSREnvironment;
49 changes: 49 additions & 0 deletions packages/@react-spectrum/test-utils/src/hydrateWorker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright 2020 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

const {JSDOM} = require('jsdom');
const {parentPort} = require('worker_threads');
const util = require('util');
require('ignore-styles');
require('@babel/register')({
extensions: ['.js', '.ts', '.tsx']
});

let {evaluate} = require('./ssrUtils');

let globalNames = new Set(Object.getOwnPropertyNames(global));

parentPort.on('message', message => {
// Setup JSDOM environment for the rendered HTML
let dom = new JSDOM(`<!doctype html><html><body><div id="root">${message.html}</div></body></html>`);

// Copy JSDOM globals into the node global object so React and others can access them.
Object.getOwnPropertyNames(dom.window).forEach(key => {
if (!globalNames.has(key)) {
Object.defineProperty(global, key, Object.getOwnPropertyDescriptor(dom.window, key));
}
});

// Capture React errors/warning and make them fail the tests.
let errors = [];
console.error = console.warn = (...messages) => {
errors.push(util.format(...messages));
};

// Evaluate the code to get a React element, and hydrate into the JSDOM.
let ReactDOM = require('react-dom');
let container = document.querySelector('#root');
let element = evaluate(message.source, message.filename);
ReactDOM.hydrate(element, container);

parentPort.postMessage({errors});
});
1 change: 1 addition & 0 deletions packages/@react-spectrum/test-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@
/// <reference types="css-module-types" />

export * from './triggerPress';
export * from './testSSR';
36 changes: 36 additions & 0 deletions packages/@react-spectrum/test-utils/src/ssrUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2020 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

import path from 'path';
import React from 'react';
import resolve from 'resolve';
import vm from 'vm';

export function evaluate(code, filename) {
// Setup a context to use as the global object when evaluating code.
// It will include React, along with a fake `require` function that resolves
// relative to the filename that's passed in (the test script.)
let ctx = {
React,
require(id) {
let resolved = resolve.sync(id, {
basedir: path.dirname(filename),
extensions: ['.js', '.json', '.ts', '.tsx']
});

return require(resolved);
}
};

vm.createContext(ctx);
return vm.runInContext(code, ctx);
}
40 changes: 40 additions & 0 deletions packages/@react-spectrum/test-utils/src/testSSR.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright 2020 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

// Can't `import` babel, have to require?
const babel = require('@babel/core');
import {evaluate} from './ssrUtils';
import ReactDOMServer from 'react-dom/server';

export async function testSSR(filename, source) {
// Transform the code with babel so JSX becomes JS.
source = babel.transformSync(source, {filename}).code;

// Evaluate the code, and render the resulting JSX element to HTML.
let element = evaluate(source, filename);
let html = ReactDOMServer.renderToString(element);

// Send the HTML along with the source code to the worker to be hydrated in a DOM environment.
return new Promise((resolve, reject) => {
let worker = global.__SSR_WORKER__;
worker.once('error', reject);
worker.once('message', message => {
if (message.errors.length > 0) {
reject(new Error(message.errors[0]));
} else {
resolve();
}
});

worker.postMessage({html, source, filename});
});
}
Loading