Playwright components testing setup (#4096)
|
@ -500,6 +500,67 @@ jobs:
|
|||
repo: context.repo.repo,
|
||||
body: 'Failed to pass the composite UI Test. If this PR is for UI change and the error is snapshot mismatch, please add "update_snapshots" label to the PR for updating the snapshot.'
|
||||
})
|
||||
components_automation_test:
|
||||
needs: get_matrix
|
||||
name: 'Components automation test (${{ matrix.flavor }})'
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix: ${{ fromJSON(needs.get_matrix.outputs.matrix) }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
- name: Restore node_modules from cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: common/temp/pnpm-store
|
||||
key: ${{ runner.os }}-${{ hashFiles('common/config/rush/pnpm-lock.yaml') }}
|
||||
- name: Install rush
|
||||
run: npm install -g @microsoft/rush
|
||||
- name: Install dependencies
|
||||
run: rush install --max-install-attempts 3
|
||||
- uses: browser-actions/setup-chrome@v1
|
||||
id: setup-chrome
|
||||
with:
|
||||
chrome-version: 120.0.6099
|
||||
- name: Switch flavor for build
|
||||
id: switch-flavor
|
||||
if: ${{ matrix.flavor != 'beta' }}
|
||||
run: |
|
||||
rush switch-flavor:${{ matrix.flavor }}
|
||||
node ./common/scripts/force-build-flavor.mjs ${{ matrix.flavor }}
|
||||
- name: Run dependency update for stable
|
||||
if: ${{ matrix.flavor == 'stable' }}
|
||||
run: rush update:stable
|
||||
- name: Components Visual Regression Tests
|
||||
id: visualregressiontests
|
||||
run: |
|
||||
cd packages/react-components
|
||||
rushx test:components
|
||||
env:
|
||||
CHROME_PATH: ${{ steps.setup-chrome.outputs.chrome-path }}
|
||||
- name: Upload snapshot diff
|
||||
if: ${{ always() && steps.visualregressiontests.outcome == 'failure' }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: snapshots
|
||||
path: packages/react-components/tests/
|
||||
- name: Comment on PR
|
||||
if: ${{ github.event_name == 'pull_request' && always() && steps.visualregressiontests.outcome == 'failure' && !contains( github.event.pull_request.labels.*.name, 'update_snapshots') }}
|
||||
uses: actions/github-script@v3
|
||||
with:
|
||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||
script: |
|
||||
github.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: 'Failed to pass the UI Test. If this PR is for UI change and the error is snapshot mismatch, please add "update_snapshots" label to the PR for updating the snapshot.'
|
||||
})
|
||||
|
||||
build_storybook:
|
||||
name: Build Storybook
|
||||
|
|
|
@ -540,6 +540,94 @@ jobs:
|
|||
git pull origin ${{ needs.get_target_branch.outputs.target }} --no-rebase --no-edit
|
||||
git push
|
||||
|
||||
components:
|
||||
needs: [precondition, get_matrix]
|
||||
if: needs.precondition.outputs.met
|
||||
name: Update packages/react-components browser test snapshots
|
||||
runs-on: ubuntu-latest
|
||||
permissions: read-all
|
||||
strategy:
|
||||
matrix: ${{ fromJSON(needs.get_matrix.outputs.matrix) }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
# Use a machine account when checking out. This is to workaround the issue where GitHub
|
||||
# actions, when using the default account, cannot trigger other actions.
|
||||
# This machine account is only for this PAT, pwd was created and thrown away
|
||||
# If an update is needed, create a new account, add access to the repo and generate a new PAT
|
||||
token: ${{ secrets.MACHINE_ACCOUNT_PAT }}
|
||||
- name: Checkout branch if on a PR
|
||||
if: ${{ github.event_name != 'workflow_dispatch' }}
|
||||
# On the `pull_request` event, actions/checkout@v3 leaves the local checkout in a detached head state.
|
||||
# Explicitly checkout the target branch so we can push later.
|
||||
run: git checkout ${{ github.event.pull_request.head.ref }}
|
||||
- name: Setup bot git information
|
||||
# User id of github actions bot. See https://api.github.com/users/better-informatics%5Bbot%5D
|
||||
run: |
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git config user.name "github-actions[bot]"
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '20.x'
|
||||
- name: Restore node_modules from cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: common/temp/pnpm-store
|
||||
key: ${{ runner.os }}-${{ hashFiles('common/config/rush/pnpm-lock.yaml') }}
|
||||
- name: Install rush
|
||||
run: npm install -g @microsoft/rush@5.112.2
|
||||
- name: Install dependencies
|
||||
run: rush install --max-install-attempts 3
|
||||
- uses: browser-actions/setup-chrome@v1
|
||||
id: setup-chrome
|
||||
with:
|
||||
chrome-version: 120.0.6099
|
||||
- name: Switch flavor for build
|
||||
if: ${{ matrix.flavor != 'beta' }}
|
||||
run: rush switch-flavor:${{ matrix.flavor }}
|
||||
- name: Update components snapshots
|
||||
id: update-components-snapshots
|
||||
run: rushx test:components:update
|
||||
working-directory: ./packages/react-components
|
||||
env:
|
||||
CHROME_PATH: ${{ steps.setup-chrome.outputs.chrome-path }}
|
||||
- name: Upload snapshot diff
|
||||
if: ${{ always() && steps.update-components-snapshots.outcome == 'failure' }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: snapshots
|
||||
path: packages/react-components/tests/temp/
|
||||
# Check if files have changed
|
||||
# More information see: https://stackoverflow.com/questions/3878624/how-do-i-programmatically-determine-if-there-are-uncommitted-changes
|
||||
- name: Check for snapshot changes
|
||||
id: changescheck
|
||||
run: |
|
||||
if [[ -z $(git status packages -s) ]]
|
||||
then
|
||||
echo "hasChanged=false" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "hasChanged=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
- name: Push new snapshots, if any
|
||||
if: ${{ steps.changescheck.outputs.hasChanged == 'true' }}
|
||||
# Before pushing changes to origin, merge any intervening changes on the upstream branch.
|
||||
# This allows multiple snapshot update jobs in this action to run concurrently.
|
||||
# - The only files updated locally are UI snapshots
|
||||
# - Each job is responsible for a unique set of UI snapshots
|
||||
# Thus we do not expect any merge conflicts due to the concurrent jobs from this workflows.
|
||||
#
|
||||
# If the UI snapshots are updated in the upstream branch while this job was running
|
||||
# (e.g., due to a merge from the base_ref for a PR), this merge will fail, and the
|
||||
# workflow will have to be triggered again. This is the desired behavior because the workflow
|
||||
# should not silently overwrite updates to the target branch.
|
||||
run: |
|
||||
git add packages/react-components/*.png
|
||||
git commit -m 'Update packages/react-components browser test snapshots'
|
||||
git pull origin ${{ needs.get_target_branch.outputs.target }} --no-rebase --no-edit
|
||||
git push
|
||||
|
||||
push_updated_snapshots:
|
||||
needs: [component_examples, html_bundle, call_composite, chat_composite, callwithchat_composite]
|
||||
if: needs.precondition.outputs.met
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"type": "none",
|
||||
"area": "feature",
|
||||
"workstream": "Reliability",
|
||||
"comment": "Playwright components testing setup",
|
||||
"packageName": "@azure/communication-react",
|
||||
"email": "98852890+vhuseinova-msft@users.noreply.github.com",
|
||||
"dependentChangeType": "none"
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"type": "none",
|
||||
"area": "feature",
|
||||
"workstream": "Reliability",
|
||||
"comment": "Playwright components testing setup",
|
||||
"packageName": "@azure/communication-react",
|
||||
"email": "98852890+vhuseinova-msft@users.noreply.github.com",
|
||||
"dependentChangeType": "none"
|
||||
}
|
|
@ -18,6 +18,7 @@
|
|||
"devDependencies": {
|
||||
"@actions/core": "^1.10.1",
|
||||
"@octokit/rest": "~19.0.3",
|
||||
"@playwright/test": "~1.39.0",
|
||||
"@rollup/plugin-commonjs": "~25.0.7",
|
||||
"@rollup/plugin-json": "^6.0.1",
|
||||
"@types/node": "^20.11.10",
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import { PlaywrightTestConfig, devices } from '@playwright/test';
|
||||
import {
|
||||
ANDROID_USER_AGENT,
|
||||
DESKTOP_4_TO_3_VIEWPORT,
|
||||
DESKTOP_USER_AGENT,
|
||||
MINUTE,
|
||||
chromeLaunchOptions,
|
||||
TestOptions
|
||||
} from './playwrightConfigConstants';
|
||||
|
||||
const isBetaBuild = process.env['COMMUNICATION_REACT_FLAVOR'] === 'beta';
|
||||
|
||||
export const config: PlaywrightTestConfig<TestOptions> = {
|
||||
// Extend per-test timeout for local debugging so that developers can single-step through
|
||||
// the test in playwright inspector.
|
||||
timeout: process.env.LOCAL_DEBUG ? 10 * MINUTE : 2 * MINUTE,
|
||||
|
||||
// Do not allow `.only` to be committed to the codebase. `.only` should only be used for diagnosing issues.
|
||||
forbidOnly: !!process.env.CI,
|
||||
|
||||
// One chance to retry a test on failure
|
||||
retries: 1,
|
||||
|
||||
// Applies to all projects
|
||||
use: {
|
||||
headless: !process.env.LOCAL_DEBUG,
|
||||
video: 'on-first-retry', // No traces on first attempt - this seems to make tests flaky.
|
||||
},
|
||||
|
||||
projects: [
|
||||
{
|
||||
name: 'Desktop Chrome',
|
||||
use: {
|
||||
viewport: DESKTOP_4_TO_3_VIEWPORT,
|
||||
launchOptions: { ...chromeLaunchOptions },
|
||||
contextOptions: {
|
||||
userAgent: DESKTOP_USER_AGENT
|
||||
},
|
||||
isBetaBuild: isBetaBuild
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Mobile Android Portrait',
|
||||
use: {
|
||||
...devices['Nexus 5'],
|
||||
launchOptions: { ...chromeLaunchOptions },
|
||||
userAgent: ANDROID_USER_AGENT,
|
||||
isBetaBuild: isBetaBuild
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Mobile Android Landscape',
|
||||
use: {
|
||||
userAgent: ANDROID_USER_AGENT,
|
||||
// Support smallest supported mobile viewport (iPhone 5/SE) ({ width: 568, height: 320 })
|
||||
viewport: { width: 568, height: 320 },
|
||||
deviceScaleFactor: 2,
|
||||
isMobile: true,
|
||||
hasTouch: true,
|
||||
defaultBrowserType: 'chromium',
|
||||
launchOptions: { ...chromeLaunchOptions },
|
||||
isBetaBuild: isBetaBuild
|
||||
}
|
||||
}
|
||||
],
|
||||
expect: {
|
||||
toMatchSnapshot: {
|
||||
maxDiffPixels: 1
|
||||
}
|
||||
}
|
||||
};
|
|
@ -0,0 +1,49 @@
|
|||
import { LaunchOptions } from '@playwright/test';
|
||||
import path from 'path';
|
||||
|
||||
export const DESKTOP_4_TO_3_VIEWPORT = {
|
||||
width: 1024,
|
||||
height: 768
|
||||
};
|
||||
|
||||
export const DESKTOP_16_TO_9_VIEWPORT = {
|
||||
width: 1024,
|
||||
height: 576
|
||||
};
|
||||
|
||||
export const chromeLaunchOptions: LaunchOptions = {
|
||||
channel: 'chrome',
|
||||
permissions: ['notifications', 'camera', 'microphone'],
|
||||
args: [
|
||||
'--font-render-hinting=none', // Ensures that fonts are rendered consistently.
|
||||
'--enable-font-antialiasing', // Ensures that fonts are rendered consistently.
|
||||
'--disable-gpu', // Ensures that fonts are rendered consistently.
|
||||
'--allow-file-access',
|
||||
'--use-fake-ui-for-media-stream',
|
||||
'--use-fake-device-for-media-stream',
|
||||
`--use-file-for-fake-video-capture=${path.join(__dirname, 'test.y4m')}`,
|
||||
'--lang=en-US',
|
||||
'--mute-audio'
|
||||
],
|
||||
ignoreDefaultArgs: [
|
||||
'--hide-scrollbars' // Don't hide scrollbars in headless mode.
|
||||
],
|
||||
// Use the CHROME_PATH environment variable if it's set, otherwise use the default installed browser by playwright.
|
||||
// We use a pinned version in GitHub actions to ensure newer versions of Chrome don't suddenly impact our tests.
|
||||
// For more information see: [Automated Tests - Pinned version of Chrome](../../docs/automated-tests.md#pinned-version-of-chrome)
|
||||
executablePath: process.env.CHROME_PATH ? process.env.CHROME_PATH : undefined
|
||||
};
|
||||
|
||||
const SECOND = 1000;
|
||||
export const MINUTE = 60 * SECOND;
|
||||
|
||||
// Using chromium useragent with a very high version to avoid breaking the unsupportedBrowser page
|
||||
export const DESKTOP_USER_AGENT = 'Windows Chrome/999.0.0.0';
|
||||
export const ANDROID_USER_AGENT = 'Android 99 Chrome/999.0.0.0 Mobile';
|
||||
|
||||
/**
|
||||
* Represents the option that should be used to skip tests for stable
|
||||
*/
|
||||
export type TestOptions = {
|
||||
isBetaBuild: boolean;
|
||||
};
|
|
@ -47,6 +47,7 @@ Most often, you should write a combination of unit-tests and hermetic browser te
|
|||
| Test Type | Characteristics | When to write |
|
||||
| ----------------- | --------------------------------- | ------------------------------------------- |
|
||||
| unit-tests | +fast, +not-flakey, -narrow | Complex business logic, helper functions |
|
||||
| components | +fast, less-flakey | Validate features for components only |
|
||||
| browser: hermetic | fast-ish, less-flakey, e2e/static | Validate features, composite snapshots |
|
||||
| browser: live | -slow, -flakey, +faithful | Smoke testing, critical user journeys |
|
||||
| storybook | ?? | ?? |
|
|
@ -90,6 +90,13 @@ module.exports = {
|
|||
env: {
|
||||
jest: true
|
||||
}
|
||||
},
|
||||
{
|
||||
// disable no-restricted-imports for playwright tests
|
||||
files: ['./tests/browser/**/*.spec.ts', './tests/browser/**/*.spec.tsx'],
|
||||
rules: {
|
||||
'no-restricted-imports': 'off'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
|
|
@ -8,4 +8,10 @@ SDK/scripts/
|
|||
SDK/cached-package.json
|
||||
|
||||
# Output files
|
||||
*.tgz
|
||||
*.tgz
|
||||
|
||||
#Playwright
|
||||
**/test-results/
|
||||
**/playwright-report/
|
||||
**/blob-report/
|
||||
**/playwright/.cache/
|
|
@ -32,7 +32,9 @@
|
|||
"_build:by-flavor": "rushx clean && rushx build:esm && rushx build:cjs && rushx _api-extractor:by-flavor",
|
||||
"_by-flavor": "rushx _current-flavor && env-cmd -f ../../common/config/env/.env --use-shell",
|
||||
"_current-flavor": "echo You are running under COMMUNICATION_REACT_FLAVOR: && env-cmd -f ../../common/config/env/.env node -p process.env.COMMUNICATION_REACT_FLAVOR",
|
||||
"_test:by-flavor": "(npm run _if-preprocess && rushx preprocess || if-env COMMUNICATION_REACT_FLAVOR=beta && echo \"skip preprocess\") && jest"
|
||||
"_test:by-flavor": "(npm run _if-preprocess && rushx preprocess || if-env COMMUNICATION_REACT_FLAVOR=beta && echo \"skip preprocess\") && jest",
|
||||
"test:components": "rushx _by-flavor \"npx playwright test -c playwright.config.components.ts\"",
|
||||
"test:components:update": "rushx _by-flavor \"npx playwright test -c playwright.config.components.ts --update-snapshots\""
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
@ -70,6 +72,7 @@
|
|||
"@babel/preset-env": "7.23.9",
|
||||
"@microsoft/api-documenter": "~7.23.20",
|
||||
"@microsoft/api-extractor": "~7.39.4",
|
||||
"@playwright/experimental-ct-react": "~1.39.0",
|
||||
"@rollup/plugin-json": "^6.0.1",
|
||||
"@testing-library/dom": "^9.3.1",
|
||||
"@testing-library/jest-dom": "^6.3.0",
|
||||
|
@ -85,6 +88,7 @@
|
|||
"@types/uuid": "^9.0.8",
|
||||
"@typescript-eslint/eslint-plugin": "^6.19.1",
|
||||
"@typescript-eslint/parser": "^6.19.1",
|
||||
"@vitejs/plugin-react": "~4.2.1",
|
||||
"ajv": "^8.12.0",
|
||||
"babel-jest": "^29.5.0",
|
||||
"babel-loader": "8.1.0",
|
||||
|
@ -105,6 +109,7 @@
|
|||
"eslint": "^7.7.0",
|
||||
"husky": "^8.0.3",
|
||||
"if-env": "^1.0.4",
|
||||
"jest-environment-jsdom": "^29.5.0",
|
||||
"jest-fetch-mock": "^3.0.3",
|
||||
"jest-junit": "^16.0.0",
|
||||
"jest": "29.7.0",
|
||||
|
@ -125,7 +130,6 @@
|
|||
"ts-node": "^10.9.2",
|
||||
"type-fest": "^3.13.1",
|
||||
"typescript": "5.3.3",
|
||||
"webpack": "5.89.0",
|
||||
"jest-environment-jsdom": "^29.5.0"
|
||||
"webpack": "5.89.0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
import { PlaywrightTestConfig, defineConfig, devices } from '@playwright/experimental-ct-react';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { TestOptions } from '../../common/config/playwright/playwrightConfigConstants';
|
||||
import { config as commonConfig } from '../../common/config/playwright/playwright.config.common';
|
||||
|
||||
const isBetaBuild = process.env['COMMUNICATION_REACT_FLAVOR'] === 'beta';
|
||||
|
||||
const componentsConfig: PlaywrightTestConfig<TestOptions> = {
|
||||
...commonConfig,
|
||||
outputDir: './tests/temp/',
|
||||
testDir: './tests/browser/',
|
||||
testMatch: '*.spec.tsx',
|
||||
snapshotDir: isBetaBuild ? './tests/snapshots/beta' : './tests/snapshots/stable'
|
||||
};
|
||||
|
||||
const ctViteConfig = {
|
||||
plugins: [
|
||||
react({
|
||||
include: /\.(js|jsx|ts|tsx)$/,
|
||||
babel: {
|
||||
// Use .babelrc files
|
||||
babelrc: true,
|
||||
// Use babel.config.js files
|
||||
configFile: true
|
||||
}
|
||||
})
|
||||
]
|
||||
};
|
||||
if (componentsConfig.use) {
|
||||
componentsConfig.use.ctViteConfig = ctViteConfig;
|
||||
} else {
|
||||
componentsConfig.use = { ctViteConfig };
|
||||
}
|
||||
|
||||
export default defineConfig<TestOptions>(componentsConfig);
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Testing Page</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="./index.tsx"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,4 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
// Apply theme here, add anything your component needs at runtime here.
|
|
@ -0,0 +1,91 @@
|
|||
# Components browser tests
|
||||
|
||||
This folder contains some Playwright browser tests for the components. See the [top-level testing documentation](../../../docs/references/automated-tests.md) to understand how these tests fit into our overall testing strategy.
|
||||
|
||||
## Folder structure
|
||||
* [../playwright.config.components.ts](../playwright.config.components.ts) Configuration for components tests
|
||||
* [../playwright](../playwright) - Files needed for Playwright component testing
|
||||
* [../playwright/.cache](../playwright/.cache) - Vite cache
|
||||
* [../tests](../tests) - Top folder for snapshot tests, snapshots and tests output
|
||||
* [../tests/browser](../tests/browser) - Components tests
|
||||
* [../tests/snapshots](../tests/snapshots) - Snapshots for the beta and stable flavors
|
||||
* [../tests/temp](../tests/temp) - Tests output (including the diff for failed tests)
|
||||
|
||||
## Run tests
|
||||
|
||||
In `packages/react-components`, run
|
||||
|
||||
```sh
|
||||
rushx test:components
|
||||
```
|
||||
The tests will be run for the current flavor.
|
||||
|
||||
## Update snapshots
|
||||
|
||||
Snapshots must only be updated as part of the CI pipeline. This is because the build agents may run on a different OS and have different graphics setup that your local machine (so there will be slight pixel differences between locally generated snapshots and ones generated by the CI pipeline).
|
||||
|
||||
To update snapshots,
|
||||
|
||||
- Add the `update_snapshots` label to your PR.
|
||||
- The [update-snapshots.yml](https://github.com/Azure/communication-ui-library/actions/workflows/update-snapshots.yml) GitHub action will generate new snapshots and push commit to your branch.
|
||||
- The workflow will remove the `update_snapshots` label from your PR. If you need to update snapshots again, you must add the label once again.
|
||||
|
||||
### Manual updates
|
||||
|
||||
As mentioned above, you should not need to update snapshots manually. This section describes the tooling available for snapshot update. This tooling is also used by the GitHub action mentioned above.
|
||||
|
||||
The CI job uses
|
||||
```sh
|
||||
rushx test:components:update
|
||||
```
|
||||
script to update the snapshots.
|
||||
|
||||
## Test development
|
||||
|
||||
The components tests are lightweight and use less resources comparing with the usual Playwright tests.
|
||||
A file with extension `.spec.tsx` should be created for new components tests.
|
||||
|
||||
### Conditional Compilation
|
||||
|
||||
Just like the rest of the UI library code, the components test use babel plugin to process the code. But the tests themselves can't use conditional compilation flags to be processed and need to use skip and fixtures feature in Playwright to be run for different flavors.
|
||||
|
||||
The fixture flag is already added to the Playwright configuration file and `FlavoredBaseTest.tsx` to be reused during future development.
|
||||
|
||||
In order to use the functionality, you will need:
|
||||
- import `test` from `FlavoredBaseTest.tsx`
|
||||
```Typescript
|
||||
import { test as betaTest } from './FlavoredBaseTest';
|
||||
```
|
||||
- add a separate `describe` block and use skip function inside it before any beta tests (there may be a few descibe block for different beta functionality)
|
||||
|
||||
```Typescript
|
||||
|
||||
//This is an example of a description with a eta only test
|
||||
betaTest.describe('TypingIndicator beta only test', () => {
|
||||
betaTest.skip(({ isBetaBuild }) => !isBetaBuild, 'The tests should be run for beta flavor only');
|
||||
|
||||
betaTest('TypingIndicator should be shown correctly 123', async ({ mount }) => {
|
||||
// some test
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Local debugging and UI mode
|
||||
|
||||
Run `rushx test:components --debug` command in Terminal from the `packages/react-components` folder to debug tests locally.
|
||||
Run `rushx test:components --ui` command in Terminal from the `packages/react-components` folder to run tests in UI mode.
|
||||
In debug mode the script runs the test under [Playwright Inspector](https://playwright.dev/docs/debug). Additionally, some internal test timeouts are relaxed to allow you to single-step through the tests via the inspector.
|
||||
|
||||
This mode requires a display device because the tests need to run in a browser with a display. In particular, this means that you can not use this mode on GitHub Codespaces.
|
||||
|
||||
To debug a particular test,
|
||||
|
||||
* Select the test to run via [`test.only`](https://playwright.dev/docs/api/class-test#test-only)
|
||||
```typescript
|
||||
test.describe('describe example', () => {
|
||||
test.only('test example', async () => {
|
||||
...
|
||||
```
|
||||
* Run the relevant test suite, usually selecting a single project: `rushx test:components --debug`
|
||||
* Single-step through the test, record a video etc through the Inspector.
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
import { test as base } from '@playwright/experimental-ct-react';
|
||||
import { TestOptions } from '../../../../common/config/playwright/playwrightConfigConstants';
|
||||
|
||||
/**
|
||||
* The test function that should be used to skip tests for stable
|
||||
*/
|
||||
export const test = base.extend<TestOptions>({
|
||||
// Define an option and provide a default value.
|
||||
// This will be overridden in the config.
|
||||
isBetaBuild: [true, { option: true }]
|
||||
});
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import React from 'react';
|
||||
import { test, expect } from '@playwright/experimental-ct-react';
|
||||
import { TypingIndicator } from '../../src/components/TypingIndicator';
|
||||
// import { test as betaTest } from './FlavoredBaseTest';
|
||||
|
||||
test.describe('TypingIndicator tests', () => {
|
||||
test('TypingIndicator should be shown correctly when 1 user is typing', async ({ mount }) => {
|
||||
const component = await mount(<TypingIndicator typingUsers={[{ userId: '1', displayName: 'User 1' }]} />);
|
||||
await component.evaluate(() => document.fonts.ready);
|
||||
await expect(component).toContainText('User 1 is typing ...');
|
||||
await expect(component).toHaveScreenshot('typing-indicator-1-user.png');
|
||||
});
|
||||
|
||||
test('TypingIndicator should be shown correctly when 5 users are typing', async ({ mount }) => {
|
||||
const users: { userId: string; displayName: string }[] = [];
|
||||
for (let i = 1; i <= 5; i++) {
|
||||
users.push({ userId: `${i}`, displayName: `User ${i}` });
|
||||
}
|
||||
const component = await mount(<TypingIndicator typingUsers={users} />);
|
||||
await component.evaluate(() => document.fonts.ready);
|
||||
await expect(component).toContainText('User 1, User 2, User 3, User 4 and 1 other are typing ...');
|
||||
await expect(component).toHaveScreenshot('typing-indicator-5-users.png');
|
||||
});
|
||||
});
|
||||
|
||||
// This is an example of beta only test, to be deleted when we have some beta tests examples
|
||||
// betaTest.describe('TypingIndicator beta only test', () => {
|
||||
// betaTest.skip(({ isStableBuild }) => isStableBuild, 'The tests should be run for beta flavor only');
|
||||
//
|
||||
// betaTest('TypingIndicator should be shown correctly 123', async ({ mount }) => {
|
||||
// const component = await mount(<TypingIndicator typingUsers={[{ userId: '1', displayName: 'User 1' }]} />);
|
||||
// await component.evaluate(() => document.fonts.ready);
|
||||
// await expect(component).toContainText('User 1 is typing ...');
|
||||
// await expect(component).toHaveScreenshot('typing-indicator-1-user-123.png');
|
||||
// });
|
||||
// });
|
После Ширина: | Высота: | Размер: 2.2 KiB |
После Ширина: | Высота: | Размер: 2.1 KiB |
После Ширина: | Высота: | Размер: 2.0 KiB |
После Ширина: | Высота: | Размер: 4.2 KiB |
После Ширина: | Высота: | Размер: 4.0 KiB |
После Ширина: | Высота: | Размер: 3.9 KiB |
После Ширина: | Высота: | Размер: 2.2 KiB |
После Ширина: | Высота: | Размер: 2.1 KiB |
После Ширина: | Высота: | Размер: 2.0 KiB |
После Ширина: | Высота: | Размер: 4.2 KiB |
После Ширина: | Высота: | Размер: 4.0 KiB |
После Ширина: | Высота: | Размер: 3.9 KiB |
|
@ -1,18 +1,13 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import { PlaywrightTestConfig, devices, ReporterDescription } from '@playwright/test';
|
||||
import path from 'path';
|
||||
|
||||
const DESKTOP_4_TO_3_VIEWPORT = {
|
||||
width: 1024,
|
||||
height: 768
|
||||
};
|
||||
|
||||
const DESKTOP_16_TO_9_VIEWPORT = {
|
||||
width: 1024,
|
||||
height: 576
|
||||
};
|
||||
import { ReporterDescription } from '@playwright/test';
|
||||
import {
|
||||
DESKTOP_16_TO_9_VIEWPORT,
|
||||
DESKTOP_USER_AGENT,
|
||||
chromeLaunchOptions
|
||||
} from '../../common/config/playwright/playwrightConfigConstants';
|
||||
import { config as commonConfig } from '../../common/config/playwright/playwright.config.common';
|
||||
|
||||
const testDir = process.env.TEST_DIR;
|
||||
if (!testDir) {
|
||||
|
@ -27,109 +22,27 @@ if (!outputDir) {
|
|||
throw new Error('Environment variable PLAYWRIGHT_OUTPUT_DIR not set');
|
||||
}
|
||||
|
||||
const chromeLaunchOptions = {
|
||||
channel: 'chrome',
|
||||
permissions: ['notifications', 'camera', 'microphone'],
|
||||
args: [
|
||||
'--font-render-hinting=none', // Ensures that fonts are rendered consistently.
|
||||
'--enable-font-antialiasing', // Ensures that fonts are rendered consistently.
|
||||
'--disable-gpu', // Ensures that fonts are rendered consistently.
|
||||
'--allow-file-access',
|
||||
'--use-fake-ui-for-media-stream',
|
||||
'--use-fake-device-for-media-stream',
|
||||
`--use-file-for-fake-video-capture=${path.join(__dirname, 'tests', 'browser', 'common', 'test.y4m')}`,
|
||||
'--lang=en-US',
|
||||
'--mute-audio'
|
||||
],
|
||||
ignoreDefaultArgs: [
|
||||
'--hide-scrollbars' // Don't hide scrollbars in headless mode.
|
||||
],
|
||||
// Use the CHROME_PATH environment variable if it's set, otherwise use the default installed browser by playwright.
|
||||
// We use a pinned version in GitHub actions to ensure newer versions of Chrome don't suddenly impact our tests.
|
||||
// For more information see: [Automated Tests - Pinned version of Chrome](../../docs/automated-tests.md#pinned-version-of-chrome)
|
||||
executablePath: process.env.CHROME_PATH ? process.env.CHROME_PATH : undefined
|
||||
};
|
||||
|
||||
const CI_REPORTERS: ReporterDescription[] = [['dot'], ['json', { outputFile: `${outputDir}/e2e-results.json` }]];
|
||||
const LOCAL_REPORTERS: ReporterDescription[] = [['list']];
|
||||
|
||||
const SECOND = 1000;
|
||||
const MINUTE = 60 * SECOND;
|
||||
|
||||
// Using chromium useragent with a very high version to avoid breaking the unsupportedBrowser page
|
||||
const DESKTOP_USER_AGENT = 'Windows Chrome/999.0.0.0';
|
||||
const ANDROID_USER_AGENT = 'Android 99 Chrome/999.0.0.0 Mobile';
|
||||
|
||||
const config: PlaywrightTestConfig = {
|
||||
const config = {
|
||||
...commonConfig,
|
||||
outputDir: outputDir,
|
||||
// Extend per-test timeout for local debugging so that developers can single-step through
|
||||
// the test in playwright inspector.
|
||||
timeout: process.env.LOCAL_DEBUG ? 10 * MINUTE : 2 * MINUTE,
|
||||
|
||||
// Do not allow `.only` to be committed to the codebase. `.only` should only be used for diagnosing issues.
|
||||
forbidOnly: !!process.env.CI,
|
||||
|
||||
// One chance to retry a test on failure
|
||||
retries: 1,
|
||||
|
||||
// Applies to all projects
|
||||
use: {
|
||||
headless: !process.env.LOCAL_DEBUG,
|
||||
video: 'on-first-retry' // No traces on first attempt - this seems to make tests flaky.
|
||||
},
|
||||
|
||||
projects: [
|
||||
{
|
||||
name: 'Desktop Chrome',
|
||||
use: {
|
||||
viewport: DESKTOP_4_TO_3_VIEWPORT,
|
||||
launchOptions: { ...chromeLaunchOptions },
|
||||
contextOptions: {
|
||||
userAgent: DESKTOP_USER_AGENT
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Desktop Chrome 16:9',
|
||||
use: {
|
||||
viewport: DESKTOP_16_TO_9_VIEWPORT,
|
||||
launchOptions: { ...chromeLaunchOptions },
|
||||
contextOptions: {
|
||||
userAgent: DESKTOP_USER_AGENT
|
||||
}
|
||||
},
|
||||
testMatch: ['OverflowGallery.test.ts']
|
||||
},
|
||||
{
|
||||
name: 'Mobile Android Portrait',
|
||||
use: {
|
||||
...devices['Nexus 5'],
|
||||
launchOptions: { ...chromeLaunchOptions },
|
||||
userAgent: ANDROID_USER_AGENT
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Mobile Android Landscape',
|
||||
use: {
|
||||
userAgent: ANDROID_USER_AGENT,
|
||||
// Support smallest supported mobile viewport (iPhone 5/SE) ({ width: 568, height: 320 })
|
||||
viewport: { width: 568, height: 320 },
|
||||
deviceScaleFactor: 2,
|
||||
isMobile: true,
|
||||
hasTouch: true,
|
||||
defaultBrowserType: 'chromium',
|
||||
launchOptions: { ...chromeLaunchOptions }
|
||||
}
|
||||
}
|
||||
],
|
||||
expect: {
|
||||
toMatchSnapshot: {
|
||||
maxDiffPixels: 1
|
||||
}
|
||||
},
|
||||
reporter: process.env.CI ? CI_REPORTERS : LOCAL_REPORTERS,
|
||||
testDir: testDir,
|
||||
snapshotDir: snapshotDir
|
||||
};
|
||||
|
||||
config.projects?.push({
|
||||
name: 'Desktop Chrome 16:9',
|
||||
use: {
|
||||
viewport: DESKTOP_16_TO_9_VIEWPORT,
|
||||
launchOptions: { ...chromeLaunchOptions },
|
||||
contextOptions: {
|
||||
userAgent: DESKTOP_USER_AGENT
|
||||
}
|
||||
},
|
||||
testMatch: ['OverflowGallery.test.ts']
|
||||
});
|
||||
|
||||
export default config;
|
||||
|
|