Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
4 changes: 4 additions & 0 deletions apps/stress-test/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"presets": ["@babel/env", "@babel/react", "@babel/typescript"],
"plugins": [["@babel/plugin-proposal-decorators", { "legacy": true }]]
}
7 changes: 7 additions & 0 deletions apps/stress-test/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": ["plugin:@fluentui/eslint-plugin/node"],
"root": true,
"rules": {
"import/no-extraneous-dependencies": ["error", { "packageDir": ["../../", "./"] }]
}
}
7 changes: 7 additions & 0 deletions apps/stress-test/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
node_modules/
dist/

benchmarks/**/*.json
benchmarks/**/results.js

yarn-error.log
29 changes: 29 additions & 0 deletions apps/stress-test/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
.storybook/
.vscode/
bundle-size/
config/
coverage/
e2e/
etc/
node_modules/
src/
dist/types/
temp/
__fixtures__
__mocks__
__tests__

*.api.json
*.log
*.spec.*
*.stories.*
*.test.*
*.yml

# config files
*config.*
*rc.*
.editorconfig
.eslint*
.git*
.prettierignore
37 changes: 37 additions & 0 deletions apps/stress-test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# @fluentui/stress-test

Stress Test is an application for testing Fluent UI components in "stressful" scenarios so we can evaluate performance.

This application is configured to support Fluent UI v8, v9 and Web Components.

Stress Test is a simple Webpack application as Webpack allows us to support Fluent v8, v9 and Web Components in a single app while using each version of Fluent natively (that is, the React versions of Fluent are typical React apps, the Web Component version is a typical web component app). Additionally, Griffel, a dependency of Fluent v9 provides some Webpack loaders so Stress Test needs to be able to use those.

## Development

Use `yarn start` and select `@fluentui/stress-test` from the list of options to start the application in development mode.

### Project layout

The project is laid out with folders for each supported version of Fluent (`v8`, `v9`, `wc`) with subfolders in each folder representing a test case. In general there should be corresponding cases between all three versions of Fluent.

The `shared` folder is for utilities that are shared across Fluent versions.

The `components` folder is also split by supported Fluent versions and is where compoenents that can be shared across test cases live.

The `benchmarks` folder houses Tachometer configurations and test results; and helper scripts for generating configurations and processing results.

### Adding test cases

Currently, new test cases must be manually added. Add tests cases to the `pages` array in `webpack.config.js` and they will be generated by Webpack. This configuration keeps the different Fluent versions in separate Webpack chunks. The dev server needs to be restarted after changes to the config file.

## Production

Run `yarn workspace @fluentui/stress-test build` from the repo root to build a static production bundle of the application.

Run `yarn workspace @fluentui/stress-test serve` to serve the static production bundle.

## Running test cases

Run `yarn workspace @fluentui/stress-test bench:*` where "\*" is one of the test cases in `package.json`.

NOTE: Stress Test must be running, preferrably as a production build.
12 changes: 12 additions & 0 deletions apps/stress-test/benchmarks/browsers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const os = require('os');

module.exports.getBrowsers = () => {
// https://github.com/Polymer/tachometer#webdriver-plugins
const browsers = ['chrome', 'firefox' /*'edge'*/];

// if (os.type() === 'Darwin') {
// browsers.push('safari');
// }

return browsers;
};
82 changes: 82 additions & 0 deletions apps/stress-test/benchmarks/simple-stress/buildConfig.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
const fs = require('fs-extra');
const { getBrowsers } = require('../browsers');
const path = require('path');
const yargs = require('yargs');

const options = yargs
.options({
size: {
alias: 's',
describe: 'Number of components to use in the test',
default: 100,
},
'add-nodes': {
alias: 'a',
describe: 'Number of components to add in the test',
},
'remove-nodes': {
alias: 'r',
describe: 'Number of components to remove in the test',
},
})
.parse();

if (!options.addNodes) {
options.addNodes = options.size;
}

if (!options.removeNodes) {
options.removeNodes = options.size - 1;
}

const makeConfigJson = (browser, testCase, sampleSize = 25) => {
const json = `
{
"$schema": "https://raw.githubusercontent.com/Polymer/tachometer/master/config.schema.json",
"sampleSize": ${sampleSize},
"timeout": 0,
"benchmarks": [
{
"browser": {
"name": "${browser}"
},
"measurement": [
{
"mode": "performance",
"entryName": "stress"
}
],
"expand": [
{
"name": "v8 - ${testCase}",
"url": "http://localhost:8080/v8/simple-stress/?test=${testCase}&numStartNodes=${options.size}&numAddNodes=${options.addNodes}&numRemoveNodes=${options.removeNodes}"
},
{
"name": "v9 - ${testCase}",
"url": "http://localhost:8080/v9/simple-stress/?test=${testCase}&numStartNodes=${options.size}&numAddNodes=${options.addNodes}&numRemoveNodes=${options.removeNodes}"
},
{
"name": "wc - ${testCase}",
"url": "http://localhost:8080/wc/simple-stress/?test=${testCase}&numStartNodes=${options.size}&numAddNodes=${options.addNodes}&numRemoveNodes=${options.removeNodes}"
}
]
}
]
}
`;

return json;
};

const browsers = getBrowsers();
const testCases = ['mount', 'inject-styles', 'prop-update', 'add-node', 'remove-node'];

fs.mkdirpSync(path.join(__dirname, 'config'));
fs.mkdirpSync(path.join(__dirname, 'results'));

for (const browser of browsers) {
for (const testCase of testCases) {
const json = makeConfigJson(browser, testCase);
fs.writeFileSync(path.join(__dirname, 'config', [browser, testCase, 'json'].join('.')), json, { encoding: 'utf8' });
}
}
34 changes: 34 additions & 0 deletions apps/stress-test/benchmarks/simple-stress/processResults.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const fs = require('fs-extra');
const { getBrowsers } = require('../browsers');
const path = require('path');

const browsers = getBrowsers();
const testCases = ['mount', 'inject-styles', 'prop-update', 'add-node', 'remove-node'];

const browserData = {};
for (const browser of browsers) {
browserData[browser] = {};
for (const testCase of testCases) {
const contents = fs.readFileSync(path.join(__dirname, 'results', `${browser}.${testCase}.results.json`), {
encoding: 'utf8',
});
const json = JSON.parse(contents);

browserData[browser][testCase] = json.benchmarks.map(test => {
return {
name: test.name,
mean: test.mean,
differences: test.differences,
samples: test.samples,
};
});
}
}

console.log(browserData);

const js = `
export const data = ${JSON.stringify(browserData, null, 4)};
`;

fs.writeFileSync(path.join(__dirname, 'results', 'results.js'), js, { encoding: 'utf8' });
6 changes: 6 additions & 0 deletions apps/stress-test/config/tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/** Jest test setup file. */

// Clean up created files/folders on exit, even after exceptions
// (will not catch SIGINT on windows)
const tmp = require('tmp');
tmp.setGracefulCleanup();
93 changes: 93 additions & 0 deletions apps/stress-test/griffelConfig.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
const { GriffelCSSExtractionPlugin } = require('@griffel/webpack-extraction-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

/**
* Webpack configuration object.
* @typedef {import('webpack').Configuration} WebpackConfig
*/

/**
* Webpack rules set.
* @typedef {import('webpack').RuleSetRule} WebpackRuleSetRule
*/

/**
* @type {string[]}
*/
const griffelModes = ['runtime', 'buildtime', 'extraction'];

/**
* @type {string}
*/
const mode = process.env.STRESS_TEST_GRIFFEL_MODE;

/**
* @type {string}
*/
const griffelMode = griffelModes.includes(mode) ? mode : 'runtime';

/**
* @type {WebpackRuleSetRule[]}
*/
const extractionRules = [
{
test: /\.(js|ts|tsx)$/,
// Apply "exclude" only if your dependencies **do not use** Griffel
// exclude: /node_modules/,
exclude: [/\.wc\.(ts|tsx)?$/, /v9\/simple\-stress/],
use: {
loader: GriffelCSSExtractionPlugin.loader,
},
},
// Add "@griffel/webpack-loader" if you use Griffel directly in your project
{
test: /\.(ts|tsx)$/,
exclude: [/node_modules/, /\.wc\.(ts|tsx)?$/],
use: {
loader: '@griffel/webpack-loader',
options: {
babelOptions: {
presets: ['@babel/preset-typescript'],
},
},
},
},
// "css-loader" is required to handle produced CSS assets by Griffel
// you can use "style-loader" or "MiniCssExtractPlugin.loader" to handle CSS insertion
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
];

/**
* Take the Webpack config object and set properties to
* configure Griffel.
*
* NOTE: this function mutates the `config` object passed in to it.
*
* @param {WebpackConfig} config - Webpack configuration object to modify.
* @returns {WebpackConfig} Modified Webpack configuration object.
*/
const configureGriffel = config => {
console.log(`Griffel running in ${griffelMode} mode.`);
if (griffelMode === 'extraction') {
if (config.module.rules) {
config.module.rules = [...extractionRules, ...config.module.rules];
} else {
config.modules.rules = extractionRules;
}

if (config.plugins) {
config.plugins = [...config.plugins, new MiniCssExtractPlugin(), new GriffelCSSExtractionPlugin()];
} else {
config.plugins = [new MiniCssExtractPlugin(), new GriffelCSSExtractionPlugin()];
}
}

return config;
};

module.exports = {
configureGriffel,
};
21 changes: 21 additions & 0 deletions apps/stress-test/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// @ts-check

/**
* @type {import('@jest/types').Config.InitialOptions}
*/
module.exports = {
displayName: 'ssr-tests-v9',
preset: '../../jest.preset.js',
globals: {
'ts-jest': {
tsConfig: '<rootDir>/tsconfig.spec.json',
diagnostics: false,
},
},
transform: {
'^.+\\.tsx?$': 'ts-jest',
},
testEnvironment: 'node',
coverageDirectory: './coverage',
setupFilesAfterEnv: ['./config/tests.js'],
};
3 changes: 3 additions & 0 deletions apps/stress-test/just.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { preset } from '@fluentui/scripts';

preset();
40 changes: 40 additions & 0 deletions apps/stress-test/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "@fluentui/stress-test",
"version": "1.0.0",
"private": true,
"main": "index.js",
"license": "MIT",
"scripts": {
"start": "webpack-dev-server --mode=development",
"build:local": "lage build --to @fluentui/react @fluentui/web-components --verbose && yarn build:app",
"build:app": "webpack --mode=production",
"serve": "http-server ./dist",
"bench:simple-stress:build": "node ./benchmarks/simple-stress/buildConfig.js",
"bench:simple-stress": "yarn bench:simple-stress:mount && yarn bench:simple-stress:inject-styles && yarn bench:simple-stress:prop-update && yarn bench:simple-stress:add-node && yarn bench:simple-stress:remove-node",
"bench:simple-stress:process": "node ./benchmarks/simple-stress/processResults.js",
"bench:simple-stress:mount": "yarn bench:simple-stress:mount:chrome && yarn bench:simple-stress:mount:firefox",
"bench:simple-stress:inject-styles": "yarn bench:simple-stress:inject-styles:chrome && yarn bench:simple-stress:inject-styles:firefox",
"bench:simple-stress:prop-update": "yarn bench:simple-stress:prop-update:chrome && yarn bench:simple-stress:prop-update:firefox",
"bench:simple-stress:add-node": "yarn bench:simple-stress:add-node:chrome && yarn bench:simple-stress:add-node:firefox",
"bench:simple-stress:remove-node": "yarn bench:simple-stress:remove-node:chrome && yarn bench:simple-stress:remove-node:firefox",
"bench:simple-stress:mount:chrome": "tach --config ./benchmarks/simple-stress/config/chrome.mount.json --json-file ./benchmarks/simple-stress/results/chrome.mount.results.json",
"bench:simple-stress:mount:firefox": "tach --config ./benchmarks/simple-stress/config/firefox.mount.json --json-file ./benchmarks/simple-stress/results/firefox.mount.results.json",
"bench:simple-stress:inject-styles:chrome": "tach --config ./benchmarks/simple-stress/config/chrome.inject-styles.json --json-file ./benchmarks/simple-stress/results/chrome.inject-styles.results.json",
"bench:simple-stress:inject-styles:firefox": "tach --config ./benchmarks/simple-stress/config/firefox.inject-styles.json --json-file ./benchmarks/simple-stress/results/firefox.inject-styles.results.json",
"bench:simple-stress:prop-update:chrome": "tach --config ./benchmarks/simple-stress/config/chrome.prop-update.json --json-file ./benchmarks/simple-stress/results/chrome.prop-update.results.json",
"bench:simple-stress:prop-update:firefox": "tach --config ./benchmarks/simple-stress/config/firefox.prop-update.json --json-file ./benchmarks/simple-stress/results/firefox.prop-update.results.json",
"bench:simple-stress:add-node:chrome": "tach --config ./benchmarks/simple-stress/config/chrome.add-node.json --json-file ./benchmarks/simple-stress/results/chrome.add-node.results.json",
"bench:simple-stress:add-node:firefox": "tach --config ./benchmarks/simple-stress/config/firefox.add-node.json --json-file ./benchmarks/simple-stress/results/firefox.add-node.results.json",
"bench:simple-stress:remove-node:chrome": "tach --config ./benchmarks/simple-stress/config/chrome.remove-node.json --json-file ./benchmarks/simple-stress/results/chrome.remove-node.results.json",
"bench:simple-stress:remove-node:firefox": "tach --config ./benchmarks/simple-stress/config/firefox.remove-node.json --json-file ./benchmarks/simple-stress/results/firefox.remove-node.results.json"
},
"dependencies": {
"@fluentui/react": "^8.90.0",
"@fluentui/react-components": "^9.2.0",
"@fluentui/react-icons": "^2.0.175",
"@fluentui/web-components": "^2.5.3",
"@microsoft/fast-element": "^1.10.4",
"react": "16.14.0",
"react-dom": "16.14.0"
}
}
Loading