Skip to content
This repository was archived by the owner on Apr 18, 2024. It is now read-only.

Commit 0eeb12d

Browse files
chore: DEV-3947: Add e2e tests coverage to reports (#1076)
* chore: DEV-3947: Tune v8 to istanbul coverage conversion * Add e2e codecov to ci * Add artifacts naming * Return jobs linking to main branch * Add support of istanbul coverage collecting * Fix imports incompatible with istanbul plugin * Add reproducible unique names in istanbulCoverage plugin * Add collecting coverege of uncovered files * Add ability to collect coverage for specific actions * Add autoloading new fragments * Adjust coverage upload * Testing the CI * Remove excess codecov config * Cleanup codecov paths * Adjust CI to support separate build for e2e/cov * Fix workflow * Fix build filename generation * Remove excess if * Fix expresssion * Alter job's name if it's for the coverage * Fix typo * Fix build command to exclude development assets * Fix build command * Cleanup * Fix testing build * Split tests and coverage upload into separate tasks * Merge coverage reports to reduce size * Fix coverage merging * Move coverage merging command to the main package * Fix CI commange for coverage merge * Remove dependencies from nyc config file * Add coverage env variable to tests run command --------- Co-authored-by: Nick Skriabin <767890+nicholasrq@users.noreply.github.com> Co-authored-by: Nick Skriabin <nr@fenelon.ru>
1 parent 9f37338 commit 0eeb12d

18 files changed

+590
-32
lines changed

.github/workflows/build_bundle.yml

+6-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ on:
66
sha:
77
required: true
88
type: string
9+
build_for_coverage:
10+
required: false
11+
type: boolean
912

1013
env:
1114
# increment it in case if you need to reset cache
@@ -14,7 +17,7 @@ env:
1417

1518
jobs:
1619
build:
17-
name: "Build"
20+
name: "Build${{ inputs.build_for_coverage && ' Coverage' || '' }}"
1821
runs-on: ubuntu-latest
1922
# ci can be skipped with `[skip ci]` prefix in message
2023
if: "!contains(github.event.head_commit.message, 'skip ci')"
@@ -62,10 +65,11 @@ jobs:
6265
env:
6366
CI: false # on true webpack breaks on warnings, and we have them a lot
6467
NODE_ENV: 'production'
68+
TEST_ENV: ${{ inputs.build_for_coverage }} # needed to create a separate build with the counters for the coverage report
6569

6670
# upload this build as artifact to current Action
6771
- name: Upload bundle
6872
uses: actions/upload-artifact@v3
6973
with:
70-
name: LSF-${{ inputs.sha }}
74+
name: LSF-${{ inputs.build_for_coverage && 'coverage-' }}${{ inputs.sha }}
7175
path: build/

.github/workflows/cicd_pipeline.yml

+22-4
Original file line numberDiff line numberDiff line change
@@ -25,23 +25,41 @@ jobs:
2525
build_bundle:
2626
name: "Build JS Bundle"
2727
if: github.event_name == 'push' || github.event.pull_request.draft == false
28-
uses: heartexlabs/label-studio-frontend/.github/workflows/build_bundle.yml@master
28+
uses: heartexlabs/label-studio-frontend/.github/workflows/build_bundle.yml@dev-3947/e2e-coverage
2929
with:
3030
sha: ${{ github.event.pull_request.head.sha || github.event.after }}
3131

32+
build_for_coverage:
33+
name: "Build JS Bundle"
34+
if: github.event_name == 'push' || github.event.pull_request.draft == false
35+
uses: heartexlabs/label-studio-frontend/.github/workflows/build_bundle.yml@dev-3947/e2e-coverage
36+
with:
37+
sha: ${{ github.event.pull_request.head.sha || github.event.after }}
38+
build_for_coverage: true
39+
3240
run_e2e:
3341
name: "Tests"
3442
if: github.event_name == 'push' || github.event.pull_request.draft == false
35-
uses: heartexlabs/label-studio-frontend/.github/workflows/e2e_tests.yml@master
43+
uses: heartexlabs/label-studio-frontend/.github/workflows/e2e_tests.yml@dev-3947/e2e-coverage
3644
needs:
37-
- build_bundle
45+
- build_for_coverage
3846
with:
3947
sha: ${{ github.event.pull_request.head.sha || github.event.after }}
4048

4149
run_unit:
4250
name: "Tests"
4351
if: github.event_name == 'push' || github.event.pull_request.draft == false
44-
uses: heartexlabs/label-studio-frontend/.github/workflows/unit_tests.yml@master
52+
uses: heartexlabs/label-studio-frontend/.github/workflows/unit_tests.yml@dev-3947/e2e-coverage
53+
with:
54+
sha: ${{ github.event.pull_request.head.sha || github.event.after }}
55+
56+
run_coverage:
57+
name: "Coverage"
58+
if: github.event_name == 'push' || github.event.pull_request.draft == false
59+
uses: heartexlabs/label-studio-frontend/.github/workflows/tests_coverage.yml@dev-3947/e2e-coverage
60+
needs:
61+
- run_unit
62+
- run_e2e
4563
with:
4664
sha: ${{ github.event.pull_request.head.sha || github.event.after }}
4765
secrets: inherit

.github/workflows/e2e_tests.yml

+33-2
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ jobs:
5656
- name: "Download bundle"
5757
uses: actions/download-artifact@v3
5858
with:
59-
name: LSF-${{ inputs.sha }}
59+
name: LSF-coverage-${{ inputs.sha }}
60+
6061
path: build/
6162

6263
# run http-server with build in background (will be killed after job ends)
@@ -86,13 +87,43 @@ jobs:
8687
8788
- name: Run e2e test suite
8889
timeout-minutes: 20 # 8-10 minutes is enough to pass tests
90+
env:
91+
NODE_ENV: 'production'
92+
TEST_ENV: true
8993
run: |
9094
set -euo pipefail
9195
cd e2e
9296
yarn run test:ci ${{ steps.cpu-info.outputs.cores-count }}
9397
94-
- uses: actions/upload-artifact@v3
98+
- name: "Upload e2e output"
99+
uses: actions/upload-artifact@v3
95100
if: ${{ failure() }}
96101
with:
97102
name: e2e output
98103
path: e2e/output/
104+
105+
- name: Merge coverage reports
106+
if: ${{ success() }}
107+
run: |
108+
set -euo pipefail
109+
yarn coverage:merge
110+
111+
- name: Upload coverage to Artifact
112+
uses: actions/upload-artifact@v3
113+
if: ${{ success() }}
114+
with:
115+
name: e2e-tests-coverage
116+
path: coverage/
117+
118+
# - name: Upload coverage to Codecov
119+
# uses: codecov/codecov-action@v3.1.1
120+
# if: ${{ !github.event.pull_request.head.repo.fork }}
121+
# with:
122+
# fail_ci_if_error: true
123+
# token: ${{ secrets.CODECOV_TOKEN }}
124+
125+
#- name: Upload coverage to artifact
126+
#uses: actions/upload-artifact@v3
127+
#with:
128+
#name: e2e-tests-coverage
129+
#path: coverage/

.github/workflows/tests_coverage.yml

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
name: "Send tests coverage"
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
sha:
7+
required: true
8+
type: string
9+
10+
jobs:
11+
run_unit_coverage_upload:
12+
name: "Upload unit coverage"
13+
runs-on: ubuntu-latest
14+
15+
# ci can be skipped with `[skip ci]` prefix in message
16+
if: "!contains(github.event.head_commit.message, 'skip ci')"
17+
18+
steps:
19+
- name: "Checkout codebase"
20+
uses: actions/checkout@v3
21+
22+
- name: "Download Unit coverage from Artifact"
23+
uses: actions/download-artifact@v3
24+
with:
25+
name: unit-tests-coverage
26+
path: coverage/
27+
28+
- name: Upload coverage to Codecov
29+
uses: codecov/codecov-action@v3.1.1
30+
if: ${{ !github.event.pull_request.head.repo.fork }}
31+
with:
32+
fail_ci_if_error: true
33+
token: ${{ secrets.codecov_token }}
34+
35+
run_e2e_coverage_upload:
36+
name: Upload E2E coverage
37+
runs-on: ubuntu-latest
38+
39+
if: "!contains(github.event.head_commit.message, 'skip ci')"
40+
41+
steps:
42+
- name: "Checkout codebase"
43+
uses: actions/checkout@v3
44+
45+
- name: "Download E2E coverage from Artifact"
46+
uses: actions/download-artifact@v3
47+
with:
48+
name: e2e-tests-coverage
49+
path: coverage/
50+
51+
- name: Upload coverage to Codecov
52+
uses: codecov/codecov-action@v3.1.1
53+
if: ${{ !github.event.pull_request.head.repo.fork }}
54+
with:
55+
fail_ci_if_error: true
56+
token: ${{ secrets.codecov_token }}
57+

.github/workflows/unit_tests.yml

+10-7
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,15 @@ jobs:
5050
- name: Run unit tests
5151
run: yarn install --frozen-lockfile && yarn test:coverage
5252

53-
- name: Upload coverage to Codecov
53+
# - name: Upload coverage to Codecov
54+
# uses: codecov/codecov-action@v3.1.1
55+
# if: ${{ !github.event.pull_request.head.repo.fork }}
56+
# with:
57+
# fail_ci_if_error: true
58+
# token: ${{ secrets.CODECOV_TOKEN }}
59+
- name: Upload coverage to Artifacts
5460
if: ${{ !github.event.pull_request.head.repo.fork }}
55-
uses: codecov/codecov-action@v3.1.1
61+
uses: actions/upload-artifact@v3
5662
with:
57-
fail_ci_if_error: true
58-
# files: ./label-studio-frontend/coverage/coverage.xml
59-
# verbose: true
60-
# name: codecov-lsf-${{ matrix.python-version }}
61-
token: ${{ secrets.CODECOV_TOKEN }}
63+
name: unit-tests-coverage
64+
path: coverage/

codecov.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
fixes:
2-
- "::src/"
2+
- "/home/runner/work/label-studio-frontend/label-studio-frontend/::"

e2e/codecept.conf.js

+12-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// HEADLESS=true npx codecept run
33
const headless = process.env.HEADLESS;
44
const port = process.env.LSF_PORT ?? 3000;
5+
const enableCoverage = process.env.COVERAGE === 'true';
56
const fs = require('fs');
67
const FRAGMENTS_PATH = './fragments/';
78

@@ -67,7 +68,6 @@ module.exports.config = {
6768
'have*',
6869
],
6970
},
70-
// For the future generations
7171
// coverage: {
7272
// enabled: true,
7373
// coverageDir: 'output/coverage',
@@ -76,6 +76,17 @@ module.exports.config = {
7676
require: './plugins/featureFlags.js',
7777
enabled: true,
7878
},
79+
istanbulCoverage: {
80+
require: './plugins/istanbulСoverage.js',
81+
enabled: enableCoverage,
82+
uniqueFileName: true,
83+
coverageDir: '../coverage',
84+
actionCoverage: {
85+
enabled: false,
86+
include: ['**/src/**'],
87+
exclude: ['**/common/**', '**/components/**'],
88+
},
89+
},
7990
screenshotOnFail: {
8091
enabled: true,
8192
},

e2e/coverage-to-istanbul.js

+59-9
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,76 @@ const path = require('path');
33

44
const v8toIstanbul = require('v8-to-istanbul');
55
const covDir = './output/coverage';
6+
const resDir = '../coverage';
7+
const basePath = path.resolve('../');
8+
const basePathRegExp = new RegExp((basePath + '\\LabelStudio').replace(/\\/g, '\\\\'), 'g');
9+
10+
const fixBasePath = (path) => {
11+
return path.replace(basePathRegExp, basePath);
12+
};
13+
14+
async function loadSource(fileName) {
15+
const source = await fs.readFile(`../build/static/js/${fileName}`);
16+
17+
return source.toString();
18+
}
19+
20+
async function loadSourceMap(fileName) {
21+
const sourceMap = await fs.readFile(`../build/static/js/${fileName}.map`);
22+
23+
return JSON.parse(sourceMap.toString().replace(/\/\.\//g, '/'));
24+
}
625

726
const convertCoverage = async (fileName) => {
8-
if (fileName.match('istanbul')) return;
27+
if (fileName.match('_final.coverage')) return;
928

1029
const coverage = require(`${covDir}/${fileName}`);
1130
const basename = path.basename(fileName).replace('.coverage.json', '');
12-
const finalName = `${covDir}/${basename}_final.coverage.json`;
31+
const finalName = path.resolve(`${resDir}/${basename}_final.coverage.json`);
1332

1433
for (const entry of coverage) {
1534
// Used to get file name
16-
const file = entry.url.match(/(?:http(s)*:\/\/.*\/)(?<file>.*)/);
17-
const converter = new v8toIstanbul(file.groups.file, 0, {
18-
source: entry.source,
19-
});
35+
const sourceFileName = entry.url.match(/(?:http(s)*:\/\/.*\/)(?<file>.*)/).groups.file;
36+
37+
if (!sourceFileName) continue;
38+
39+
const scriptSource = await loadSource(sourceFileName);
40+
const scriptSourceMap = await loadSourceMap(sourceFileName);
41+
42+
const filePath = path.resolve(`../${sourceFileName}`);
43+
44+
const converter = new v8toIstanbul(filePath, 0,
45+
{
46+
source: scriptSource.toString(),
47+
sourceMap: {
48+
sourcemap: scriptSourceMap,
49+
},
50+
},
51+
);
2052

2153
await converter.load();
2254
converter.applyCoverage(entry.functions);
2355

56+
const result = JSON.stringify(converter.toIstanbul(), (key, value) => {
57+
if (key === '') {
58+
return Object.fromEntries(Object.entries(value).reduce((res, [key, val]) => {
59+
res.push([
60+
fixBasePath(key),
61+
val,
62+
]);
63+
return res;
64+
}, []));
65+
}
66+
if (key === 'path') {
67+
return fixBasePath(value);
68+
}
69+
return value;
70+
}, 2);
71+
2472
// Store converted coverage file which can later be used to generate report
2573
await fs.writeFile(
2674
finalName,
27-
JSON.stringify(converter.toIstanbul(), null, 2),
75+
result,
2876
);
2977
console.log(`Processed ${basename}`);
3078
}
@@ -33,6 +81,8 @@ const convertCoverage = async (fileName) => {
3381
};
3482

3583
// read all the coverage file from output/coverage folder
36-
fs.readdir(covDir).then(files => {
37-
files.forEach(convertCoverage);
84+
fs.readdir(covDir).then(async files => {
85+
for (const file of files) {
86+
await convertCoverage(file);
87+
}
3888
});

e2e/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"test": "codeceptjs run",
99
"test:local": "HEADLESS=true yarn test",
1010
"test:parallel": "codeceptjs run-workers",
11-
"test:ci": "HEADLESS=true yarn run test:parallel",
11+
"test:ci": "HEADLESS=true COVERAGE=true yarn run test:parallel",
1212
"test:ci:no-worker": "HEADLESS=true codeceptjs run",
1313
"coverage:istanbul": "node ./coverage-to-istanbul.js",
1414
"coverage:report": "nyc report --reporter=html --include=../src/**/* --report-dir=./coverage --temp-dir=./output/coverage",

0 commit comments

Comments
 (0)