Skip to content

Commit 9e00738

Browse files
authored
feat: add tests and example jest config (#1987)
* feat: add tests and example jest config * added tests for GraphHelper utils * ci: adding test reporting and coverage commenting * reorder ci steps{ * ci: enable caching of node_modules * ci: fix missing brace * refactor: restored test to operation * adding upload steps to archive test results for dependent workflow * add working branch to PR list * removing pack step * reordering steps to test summary output * pivot trigger to pull_request_target * removing archival steps * refactoring to publish comments from dependent workflow * simplify run condition * pivot reporting workflow to trigger on completed workflows * simplify run condition * adding a name to the pr reporting workflow * removing path from artifact download * trigger workflow * simplify conditionals for archiving * adding lerna install for consistency with other pipelines * Expanding comments * comment * adding a test for the EventDispatcher and removing a duplicate delay function * added two simple component tests and example of mocking http requests * fixing branch filter for reporting * setting up exclusions to omit some files from test coverage * correcting mgt-person tests after update from main * refactor: move MockProvider and MockGraph back to mgt-element for testing purposes
1 parent 5ac2099 commit 9e00738

File tree

29 files changed

+32641
-45
lines changed

29 files changed

+32641
-45
lines changed

.github/workflows/pr-reporting.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: PR Quality Reporting
2+
3+
on:
4+
workflow_run:
5+
workflows: [Build pr]
6+
types:
7+
- completed
8+
branches: [main, release/**, next/**]
9+
10+
jobs:
11+
pr_report:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: Test Report
15+
uses: dorny/test-reporter@v1
16+
if: always() # run this step even if previous step failed
17+
with:
18+
artifact: test-results
19+
name: JEST Tests # Name of the check run which will be created
20+
path: junit.xml # Path to test results
21+
reporter: jest-junit # Format of test results
22+
23+
- name: Code Coverage Summary Report
24+
uses: irongut/[email protected]
25+
with:
26+
filename: coverage/cobertura-coverage.xml
27+
badge: true
28+
format: 'markdown'
29+
output: 'both'
30+
- name: Add Coverage PR Comment
31+
uses: marocchino/sticky-pull-request-comment@v2
32+
if: github.event_name == 'pull_request'
33+
with:
34+
recreate: true
35+
path: code-coverage-results.md

.github/workflows/pr.yml

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
22
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3+
# Tests are executed and coverage reports are emitted as to the action summary
4+
# A dependent workflow which uses workflow_run as a trigger reads the archived outputs and emits comments to the PR triggering this build
35

46
name: Build pr
57

68
on:
79
pull_request:
8-
branches: [main, release/**, next/**]
10+
branches: [main, release/**, next/**, feat/add-jest-tests]
911

1012
jobs:
1113
build:
@@ -21,21 +23,42 @@ jobs:
2123
uses: actions/setup-node@v1
2224
with:
2325
node-version: ${{ matrix.node-version }}
24-
- run: npm install -g yarn
25-
- run: yarn
26+
- name: Cache Node Modules
27+
id: cache-node-modules
28+
uses: actions/cache@v2
29+
with:
30+
path: |
31+
node_modules
32+
*/*/node_modules
33+
key: ${{ runner.os }}-${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}
34+
- run: npm install -g yarn lerna
35+
- name: Install node_modules
36+
if: steps.cache-node-modules.outputs.cache-hit != 'true'
37+
run: yarn
2638
- run: node scripts/setVersion.js --next
2739
- run: yarn run tsc -v
2840
- run: yarn build
29-
- run: yarn run pack
30-
31-
- name: Upload a Build Artifact - package
32-
uses: actions/upload-artifact@v2
41+
- run: yarn test
42+
- name: Archive test results
43+
uses: actions/upload-artifact@v2 # upload test results
44+
if: always() # run this step even if previous step failed
3345
with:
34-
name: npm-packages
35-
path: artifacts/*
46+
name: test-results
47+
path: testResults/junit.xml
48+
- name: Archive coverage report
49+
uses: actions/upload-artifact@v2 # upload coverage report
50+
if: always() # run this step even if previous step failed
51+
with:
52+
name: coverage
53+
path: coverage/cobertura-coverage.xml
3654

37-
- name: Upload a Build Artifact - bundle
38-
uses: actions/upload-artifact@v2
55+
- name: Code Coverage Summary Report
56+
uses: irongut/[email protected]
3957
with:
40-
name: bundle
41-
path: packages/mgt/dist/bundle
58+
filename: coverage/cobertura-coverage.xml
59+
badge: true
60+
format: 'markdown'
61+
output: 'both'
62+
63+
- name: Write to Job Summary
64+
run: cat code-coverage-results.md >> $GITHUB_STEP_SUMMARY

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ assets/mgt.storybook.js
77
*.sw[mnpcod]
88
*.log
99
*.lock
10+
# explicitly allow yarn lock to facilitate pipeline caching of node_modules
11+
!yarn.lock
1012
*.tmp
1113
*.tmp.*
1214
log.txt
@@ -32,7 +34,7 @@ UserInterfaceState.xcuserstate
3234
# testing
3335
coverage/
3436
*.tgz
35-
testResults/junit.xml
37+
testResults/
3638

3739
*-css.ts
3840
*.tsbuildinfo

.vscode/launch.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,15 @@
1919
"url": "http://localhost:3000",
2020
"webRoot": "${workspaceFolder}",
2121
"version": "dev"
22+
},
23+
{
24+
"name": "Debug Jest Tests",
25+
"type": "node",
26+
"request": "launch",
27+
"runtimeArgs": ["--inspect-brk", "${workspaceRoot}/node_modules/jest/bin/jest.js", "--runInBand"],
28+
"console": "integratedTerminal",
29+
"internalConsoleOptions": "neverOpen",
30+
"port": 9229
2231
}
2332
]
2433
}

jest-setup.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import '@testing-library/jest-dom/extend-expect';

jest.config.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// These files are know to be ESM and should be transformed by ts-jest
2+
const esModules = [
3+
'msal',
4+
'@open-wc',
5+
'@lit',
6+
'lit',
7+
'testing-library__dom',
8+
'@microsoft/microsoft-graph-client',
9+
'@microsoft/mgt-element',
10+
'@microsoft/mgt-components',
11+
'@microsoft/mgt-msal-provider'
12+
].join('|');
13+
14+
const config = {
15+
collectCoverage: true,
16+
coverageReporters: ['cobertura', 'html'],
17+
coveragePathIgnorePatterns: ['/node_modules/', '/dist/', '-css.ts', '/__test_data/'],
18+
reporters: [
19+
'default',
20+
[
21+
'jest-junit',
22+
{
23+
outputDirectory: 'testResults',
24+
outputName: 'junit.xml'
25+
}
26+
]
27+
],
28+
transformIgnorePatterns: [`<rootDir>/node_modules/(?!${esModules})`],
29+
testEnvironment: 'jsdom',
30+
testMatch: ['**/*.tests.{ts,tsx}'],
31+
setupFiles: ['whatwg-fetch'], // polyfill fetch for Graph Client,
32+
setupFilesAfterEnv: ['./jest-setup.js'],
33+
transform: {
34+
// '^.+\\.[tj]sx?$' to process js/ts with `ts-jest`
35+
// '^.+\\.m?[tj]sx?$' to process js/ts/mjs/mts with `ts-jest`
36+
'^.+\\.[tj]sx?$': [
37+
'ts-jest',
38+
{
39+
useESM: false, // transpile ESM to CJS for Jest
40+
tsconfig: './tsconfig.test.json'
41+
}
42+
]
43+
}
44+
};
45+
module.exports = config;

package.json

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
"storybook:build": "npm run storybook:bundle && npm run storybook:dev && build-storybook -s assets && cpx .storybook/CNAME storybook-static",
5959
"storybook:deploy": "npm run storybook:build && storybook-to-ghpages -e storybook-static",
6060
"setLicense": "gulp setLicense",
61+
"test": "jest",
6162
"version:tsc": "tsc -v"
6263
},
6364
"storybook-deployer": {
@@ -74,6 +75,7 @@
7475
"@babel/preset-react": "^7.12.7",
7576
"@babel/preset-typescript": "^7.12.7",
7677
"@octokit/rest": "^18.5.3",
78+
"@open-wc/testing-helpers": "^2.1.4",
7779
"@storybook/addon-a11y": "^6.4.4",
7880
"@storybook/addon-actions": "^6.4.4",
7981
"@storybook/addon-docs": "^6.4.4",
@@ -83,7 +85,10 @@
8385
"@storybook/cli": "^6.4.4",
8486
"@storybook/storybook-deployer": "^2.8.10",
8587
"@storybook/web-components": "^6.4.4",
86-
"@types/jest": "^24.9.1",
88+
"@testing-library/dom": "^8.20.0",
89+
"@testing-library/jest-dom": "^5.16.5",
90+
"@testing-library/user-event": "^14.4.3",
91+
"@types/jest": "^29.2.4",
8792
"@types/node": "12.12.22",
8893
"@types/react": "^17.0.00",
8994
"@types/react-dom": "^17.0.0",
@@ -102,8 +107,10 @@
102107
"gulp-sass": "^5.1.0",
103108
"gulp-util": "^3.0.8",
104109
"husky": "^4.3.0",
105-
"jest": "^24.5.0",
106-
"jest-junit": "^6.3.0",
110+
"jest": "^29.3.1",
111+
"jest-environment-jsdom": "^29.3.1",
112+
"jest-fetch-mock": "^3.0.3",
113+
"jest-junit": "^15.0.0",
107114
"lerna": "^3.22.1",
108115
"lit": "^2.3.1",
109116
"monaco-editor": "^0.19.3",
@@ -126,13 +133,14 @@
126133
"sass": "^1.29.0",
127134
"shx": "^0.3.3",
128135
"storybook-addon-web-components-knobs": "^0.3.20",
129-
"ts-jest": "^26.5.5",
136+
"testing-library__dom": "^7.29.4-beta.1",
137+
"ts-jest": "^29.0.3",
130138
"tslint": "^6.1.3",
131139
"tslint-config-prettier": "^1.18.0",
132140
"tslint-microsoft-contrib": "^6.2.0",
133141
"typescript": "^4.8.2",
134142
"web-component-analyzer": "^1.1.6",
135-
"whatwg-fetch": "^3.5.0"
143+
"whatwg-fetch": "^3.6.2"
136144
},
137145
"husky": {
138146
"hooks": {

packages/mgt-components/src/components/PersonCardInteraction.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
* -------------------------------------------------------------------------------------------
66
*/
77

8+
/* istanbul ignore file */
9+
810
/**
911
* Defines how a person card is shown when a user interacts with
1012
* a person component

packages/mgt-components/src/components/mgt-person/__test_data/mock-responses.ts

Lines changed: 51 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/**
2+
* -------------------------------------------------------------------------------------------
3+
* Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License.
4+
* See License in the project root for license information.
5+
* -------------------------------------------------------------------------------------------
6+
*/
7+
8+
import { screen } from 'testing-library__dom';
9+
import { fixture } from '@open-wc/testing-helpers';
10+
import { enableFetchMocks } from 'jest-fetch-mock';
11+
import fetchMock from 'jest-fetch-mock';
12+
import { MockProvider, Providers } from '@microsoft/mgt-element';
13+
import { userPhotoBatchResponse } from './__test_data/mock-responses';
14+
import './mgt-person';
15+
enableFetchMocks();
16+
17+
let person: Element;
18+
describe('mgt-person - tests', () => {
19+
beforeEach(async () => {
20+
fetchMock.resetMocks();
21+
fetchMock
22+
// Response for the setup of the MockGraph to call to the proxy service
23+
.once(() =>
24+
Promise.resolve({
25+
headers: {
26+
'Content-Type': 'application/json'
27+
},
28+
status: 200,
29+
body: 'https://fake-proxy.microsoft.com/'
30+
})
31+
)
32+
// response to the $batch request to load the user photo and user data when there is nothing in the cache
33+
// note that this used mockOnceIf matching the fake url supplied in the first mock.
34+
.mockOnceIf(/https:\/\/fake-proxy.microsoft.com\/*$/, () =>
35+
Promise.resolve({
36+
headers: {
37+
'Content-Type': 'application/json'
38+
},
39+
status: 200,
40+
body: JSON.stringify(userPhotoBatchResponse)
41+
})
42+
);
43+
});
44+
45+
it('should render', async () => {
46+
Providers.globalProvider = new MockProvider(true);
47+
person = await fixture('<mgt-person person-query="me" view="twoLines"></mgt-person>');
48+
const img = await screen.findAllByAltText('Photo for Megan Bowen');
49+
expect(img).not.toBeNull();
50+
});
51+
52+
it('should pop up a flyout on click', async () => {
53+
Providers.globalProvider = new MockProvider(true);
54+
person = await fixture('<mgt-person person-query="me" view="twoLines" person-card="click"></mgt-person>');
55+
const img = await screen.findAllByAltText('Photo for Megan Bowen');
56+
expect(img).not.toBeNull();
57+
expect(img.length).toBe(1);
58+
// test that there is no flyout
59+
expect(screen.queryByTestId('flyout-slot')).toBeNull();
60+
61+
img[0].click();
62+
63+
expect(screen.queryByTestId('flyout-slot')).toBeDefined();
64+
});
65+
});

0 commit comments

Comments
 (0)