Skip to content

Commit c72b25c

Browse files
authored
Merge branch 'master' into dependabot/npm_and_yarn/vue-3.3.8
2 parents 8fe8695 + 4b0abdf commit c72b25c

File tree

9 files changed

+285
-123
lines changed

9 files changed

+285
-123
lines changed

.github/dependabot.yml

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ updates:
55
schedule:
66
interval: 'weekly'
77
open-pull-requests-limit: 10
8+
rebase-strategy: 'disabled'
89
labels:
910
- 'pr:daveit'
1011
- 'pr:e2e'
@@ -34,6 +35,7 @@ updates:
3435
directory: '/'
3536
schedule:
3637
interval: 'daily'
38+
rebase-strategy: 'disabled'
3739
labels:
3840
- 'pr:daveit'
3941
- 'type:maintenance'

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ Each test suite generates a report in CircleCI. For a complete overview of testi
127127

128128
Our code coverage is generated during the runtime of our unit, e2e, and visual tests. The combination of those reports is published to [codecov.io](https://app.codecov.io/gh/nasa/openmct/)
129129

130+
For more on the specifics of our code coverage setup, [see](TESTING.md#code-coverage)
131+
130132
# Glossary
131133

132134
Certain terms are used throughout Open MCT with consistent meanings

TESTING.md

+77-6
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,85 @@ Documentation located [here](./e2e/README.md)
3737

3838
## Code Coverage
3939

40-
* 100% statement coverage is achievable and desirable.
40+
It's up to the individual developer as to whether they want to add line coverage in the form of a unit test or e2e test.
4141

42-
Codecov.io will combine each of the above commands with [Codecov.io Flags](https://docs.codecov.com/docs/flags). Effectively, this allows us to combine multiple reports which are run at various stages of our CI Pipeline or run as part of a parallel process.
42+
Line Code Coverage is generated by our unit tests and e2e tests, then combined by ([Codecov.io Flags](https://docs.codecov.com/docs/flags)), and finally reported in GitHub PRs by Codecov.io's PR Bot. This workflow gives a comprehensive (if flawed) view of line coverage.
43+
44+
### Karma-istanbul
45+
46+
Line coverage is generated by our `karma-coverage-istanbul-reporter` package as defined in our `karma.conf.js` file:
47+
48+
```js
49+
coverageIstanbulReporter: {
50+
fixWebpackSourcePaths: true,
51+
skipFilesWithNoCoverage: true,
52+
dir: 'coverage/unit', //Sets coverage file to be consumed by codecov.io
53+
reports: ['lcovonly']
54+
},
55+
```
56+
57+
Once the file is generated, it can be published to codecov with
58+
59+
```json
60+
"cov:unit:publish": "codecov --disable=gcov -f ./coverage/unit/lcov.info -F unit",
61+
```
62+
63+
### e2e
64+
The e2e line coverage is a bit more complex than the karma implementation. This is the general sequence of events:
65+
66+
1. Each e2e suite will start webpack with the ```npm run start:coverage``` command with config `webpack.coverage.js` and the `babel-plugin-istanbul` plugin to generate code coverage during e2e test execution using our custom [baseFixture](./baseFixtures.js).
67+
1. During testcase execution, each e2e shard will generate its piece of the larger coverage suite. **This coverage file is not merged**. The raw coverage file is stored in a `.nyc_report` directory.
68+
1. [nyc](https://github.com/istanbuljs/nyc) converts this directory into a `lcov` file with the following command `npm run cov:e2e:report`
69+
1. Most of the tests are run in the '@stable' configuration and focus on chrome/ubuntu at a single resolution. This coverage is published to codecov with `npm run cov:e2e:stable:publish`.
70+
1. The rest of our coverage only appears when run against `@unstable` tests, persistent datastore (couchdb), non-ubuntu machines, and non-chrome browsers with the `npm run cov:e2e:full:publish` flag. Since this happens about once a day, we have leveraged codecov.io's carryforward flag to report on lines covered outside of each commit on an individual PR.
4371

44-
This e2e coverage is combined with our unit test report to give a comprehensive (if flawed) view of line coverage.
4572

4673
### Limitations in our code coverage reporting
74+
Our code coverage implementation has some known limitations:
75+
- [Variability](https://github.com/nasa/openmct/issues/5811)
76+
- [Accuracy](https://github.com/nasa/openmct/issues/7015)
77+
- [Vue instrumentation gaps](https://github.com/nasa/openmct/issues/4973)
78+
79+
## Troubleshooting CI
80+
The following is an evolving guide to troubleshoot CI and PR issues.
81+
82+
### Github Checks failing
83+
There are a few reasons that your GitHub PR could be failing beyond simple failed tests.
84+
* Required Checks. We're leveraging required checks in GitHub so that we can quickly and precisely control what becomes and informational failure vs a hard requirement. The only way to determine the difference between a required vs information check is check for the `(Required)` emblem next to the step details in GitHub Checks.
85+
* Not all required checks are run per commit. You may need to manually trigger addition GitHub checks with a `pr:<label>` label added to your PR.
86+
87+
### Flaky tests
88+
There are two ways to know if a test on your branch is historically flaky:
89+
1. `deploysentinel`'s PR comment bot to give an accurate and historical view of e2e flakiness. Check your PR for a view of the test failures and flakes (with link to the failing test). Note: only a 7 day window of flake is available.
90+
2. (CircleCI's test insights feature)[https://circleci.com/blog/introducing-test-insights-with-flaky-test-detection/] collects historical data about the individual test results for both unit and e2e tests. Note: only a 14 day window of flake is available.
91+
92+
### Local=Pass and CI=Fail
93+
Although rare, it is possible that your test can pass locally but fail in CI.
94+
95+
#### Busting Cache
96+
In certain circumstances, the CircleCI cache can become stale. In order to bust the cache, we've implemented a runtime boolean parameter in Circle CI creatively name BUST_CACHE. To execute:
97+
1. Navigate to the branch in Circle CI believed to have stale cache.
98+
1. Click on the 'Trigger Pipeline' button.
99+
1. Add Parameter -> Parameter Type = boolean , Name = BUST_CACHE ,Value = true
100+
1. Click 'Trigger Pipeline'
101+
102+
#### Run tests in the same container as CI
103+
104+
In extreme cases, tests can fail due to the constraints of running within a container. To execute tests in exactly the same way as run in CircleCI.
105+
106+
```sh
107+
// Replace {X.X.X} with the current Playwright version
108+
// from our package.json or circleCI configuration file
109+
docker run --rm --network host --cpus="2" -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/playwright:v{X.X.X}-focal /bin/bash
110+
npm install
111+
```
112+
113+
At this point, you're running inside the same container and with 2 cpu cores. You can specify the unit tests:
114+
```sh
115+
npm run test
116+
```
117+
or e2e tests:
47118

48-
Our code coverage implementation has two known limitations:
49-
- [Variability and accuracy](https://github.com/nasa/openmct/issues/5811)
50-
- [Vue instrumentation](https://github.com/nasa/openmct/issues/4973)
119+
```sh
120+
npx playwright test --config=e2e/playwright-ci.config.js --project=chrome --grep <the testcase name>
121+
```

e2e/README.md

+4-12
Original file line numberDiff line numberDiff line change
@@ -490,15 +490,7 @@ Our e2e code coverage is captured and combined with our unit test coverage. For
490490
491491
#### Generating e2e code coverage
492492
493-
Code coverage is collected during test execution using our custom [baseFixture](./baseFixtures.js). The raw coverage files are stored in a `.nyc_report` directory to be converted into a lcov file with the following [nyc](https://github.com/istanbuljs/nyc) command:
494-
495-
```npm run cov:e2e:report```
496-
497-
At this point, the nyc linecov report can be published to [codecov.io](https://about.codecov.io/) with the following command:
498-
499-
```npm run cov:e2e:stable:publish``` for the stable suite running in ubuntu.
500-
or
501-
```npm run cov:e2e:full:publish``` for the full suite running against all available platforms.
493+
Please read more about our code coverage [here](../TESTING.md#code-coverage)
502494
503495
## Other
504496
@@ -548,10 +540,10 @@ A single e2e test in Open MCT is extended to run:
548540
- How is Open MCT extending default Playwright functionality?
549541
- What about Component Testing?
550542
551-
### Troubleshooting
543+
### e2e Troubleshooting
544+
545+
Please follow the general guide troubleshooting in [the general troubleshooting doc](../TESTING.md#troubleshooting-ci)
552546
553-
- Why is my test failing on CI and not locally?
554-
- How can I view the failing tests on CI?
555547
- Tests won't start because 'Error: <http://localhost:8080/># is already used...'
556548
This error will appear when running the tests locally. Sometimes, the webserver is left in an orphaned state and needs to be cleaned up. To clear up the orphaned webserver, execute the following from your Terminal:
557549
```lsof -n -i4TCP:8080 | awk '{print$2}' | tail -1 | xargs kill -9```

e2e/tests/functional/plugins/lad/lad.e2e.spec.js

+105-11
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,24 @@ const {
2525
createDomainObjectWithDefaults,
2626
setStartOffset,
2727
setFixedTimeMode,
28-
setRealTimeMode
28+
setRealTimeMode,
29+
openObjectTreeContextMenu
2930
} = require('../../../../appActions');
3031

3132
test.describe('Testing LAD table configuration', () => {
33+
let ladTable;
34+
let sineWaveObject;
3235
test.beforeEach(async ({ page }) => {
3336
await page.goto('./', { waitUntil: 'domcontentloaded' });
3437

3538
// Create LAD table
36-
const ladTable = await createDomainObjectWithDefaults(page, {
39+
ladTable = await createDomainObjectWithDefaults(page, {
3740
type: 'LAD Table',
3841
name: 'Test LAD Table'
3942
});
4043

4144
// Create Sine Wave Generator
42-
await createDomainObjectWithDefaults(page, {
45+
sineWaveObject = await createDomainObjectWithDefaults(page, {
4346
type: 'Sine Wave Generator',
4447
name: 'Test Sine Wave Generator',
4548
parent: ladTable.uuid
@@ -50,31 +53,51 @@ test.describe('Testing LAD table configuration', () => {
5053
test('in edit mode, LAD Tables provide ability to hide columns', async ({ page }) => {
5154
// Edit LAD table
5255
await page.locator('[title="Edit"]').click();
53-
54-
// // Expand the 'My Items' folder in the left tree
55-
// await page.locator('.c-tree__item__view-control.c-disclosure-triangle').click();
56-
// // Add the Sine Wave Generator to the LAD table and save changes
57-
// await page.dragAndDrop('role=treeitem[name=/Test Sine Wave Generator/]', '.c-lad-table-wrapper');
58-
// select configuration tab in inspector
5956
await page.getByRole('tab', { name: 'LAD Table Configuration' }).click();
6057

6158
// make sure headers are visible initially
6259
await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeVisible();
6360
await expect(page.getByRole('cell', { name: 'Units' })).toBeVisible();
6461
await expect(page.getByRole('cell', { name: 'Type' })).toBeVisible();
62+
await expect(page.getByRole('cell', { name: 'WATCH' })).toBeVisible();
63+
await expect(page.getByRole('cell', { name: 'WARNING' })).toBeVisible();
64+
await expect(page.getByRole('cell', { name: 'DISTRESS' })).toBeVisible();
65+
await expect(page.getByRole('cell', { name: 'CRITICAL' })).toBeVisible();
66+
await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible();
6567

6668
// hide timestamp column
6769
await page.getByLabel('Timestamp').uncheck();
6870
await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeHidden();
6971
await expect(page.getByRole('cell', { name: 'Units' })).toBeVisible();
7072
await expect(page.getByRole('cell', { name: 'Type' })).toBeVisible();
73+
await expect(page.getByRole('cell', { name: 'WATCH' })).toBeVisible();
74+
await expect(page.getByRole('cell', { name: 'WARNING' })).toBeVisible();
75+
await expect(page.getByRole('cell', { name: 'DISTRESS' })).toBeVisible();
76+
await expect(page.getByRole('cell', { name: 'CRITICAL' })).toBeVisible();
77+
await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible();
7178

7279
// hide units & type column
7380
await page.getByLabel('Units').uncheck();
7481
await page.getByLabel('Type').uncheck();
7582
await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeHidden();
7683
await expect(page.getByRole('cell', { name: 'Units' })).toBeHidden();
7784
await expect(page.getByRole('cell', { name: 'Type' })).toBeHidden();
85+
await expect(page.getByRole('cell', { name: 'WATCH' })).toBeVisible();
86+
await expect(page.getByRole('cell', { name: 'WARNING' })).toBeVisible();
87+
await expect(page.getByRole('cell', { name: 'DISTRESS' })).toBeVisible();
88+
await expect(page.getByRole('cell', { name: 'CRITICAL' })).toBeVisible();
89+
await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible();
90+
91+
// hide WATCH column
92+
await page.getByLabel('WATCH').uncheck();
93+
await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeHidden();
94+
await expect(page.getByRole('cell', { name: 'Units' })).toBeHidden();
95+
await expect(page.getByRole('cell', { name: 'Type' })).toBeHidden();
96+
await expect(page.getByRole('cell', { name: 'WATCH' })).toBeHidden();
97+
await expect(page.getByRole('cell', { name: 'WARNING' })).toBeVisible();
98+
await expect(page.getByRole('cell', { name: 'DISTRESS' })).toBeVisible();
99+
await expect(page.getByRole('cell', { name: 'CRITICAL' })).toBeVisible();
100+
await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible();
78101

79102
// save and reload and verify they columns are still hidden
80103
await page.locator('button[title="Save"]').click();
@@ -83,6 +106,11 @@ test.describe('Testing LAD table configuration', () => {
83106
await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeHidden();
84107
await expect(page.getByRole('cell', { name: 'Units' })).toBeHidden();
85108
await expect(page.getByRole('cell', { name: 'Type' })).toBeHidden();
109+
await expect(page.getByRole('cell', { name: 'WATCH' })).toBeHidden();
110+
await expect(page.getByRole('cell', { name: 'WARNING' })).toBeVisible();
111+
await expect(page.getByRole('cell', { name: 'DISTRESS' })).toBeVisible();
112+
await expect(page.getByRole('cell', { name: 'CRITICAL' })).toBeVisible();
113+
await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible();
86114

87115
// Edit LAD table
88116
await page.locator('[title="Edit"]').click();
@@ -93,25 +121,41 @@ test.describe('Testing LAD table configuration', () => {
93121
await expect(page.getByRole('cell', { name: 'Units' })).toBeHidden();
94122
await expect(page.getByRole('cell', { name: 'Type' })).toBeHidden();
95123
await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeVisible();
124+
await expect(page.getByRole('cell', { name: 'WATCH' })).toBeHidden();
125+
await expect(page.getByRole('cell', { name: 'WARNING' })).toBeVisible();
126+
await expect(page.getByRole('cell', { name: 'DISTRESS' })).toBeVisible();
127+
await expect(page.getByRole('cell', { name: 'CRITICAL' })).toBeVisible();
128+
await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible();
96129

97-
// save and reload and make sure only timestamp is still visible
130+
// save and reload and make sure timestamp is still visible
98131
await page.locator('button[title="Save"]').click();
99132
await page.locator('text=Save and Finish Editing').click();
100133
await page.reload();
101134
await expect(page.getByRole('cell', { name: 'Units' })).toBeHidden();
102135
await expect(page.getByRole('cell', { name: 'Type' })).toBeHidden();
103136
await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeVisible();
137+
await expect(page.getByRole('cell', { name: 'WATCH' })).toBeHidden();
138+
await expect(page.getByRole('cell', { name: 'WARNING' })).toBeVisible();
139+
await expect(page.getByRole('cell', { name: 'DISTRESS' })).toBeVisible();
140+
await expect(page.getByRole('cell', { name: 'CRITICAL' })).toBeVisible();
141+
await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible();
104142

105143
// Edit LAD table
106144
await page.locator('[title="Edit"]').click();
107145
await page.getByRole('tab', { name: 'LAD Table Configuration' }).click();
108146

109-
// show units and type columns
147+
// show units, type, and WATCH columns
110148
await page.getByLabel('Units').check();
111149
await page.getByLabel('Type').check();
150+
await page.getByLabel('WATCH').check();
112151
await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeVisible();
113152
await expect(page.getByRole('cell', { name: 'Units' })).toBeVisible();
114153
await expect(page.getByRole('cell', { name: 'Type' })).toBeVisible();
154+
await expect(page.getByRole('cell', { name: 'WATCH' })).toBeVisible();
155+
await expect(page.getByRole('cell', { name: 'WARNING' })).toBeVisible();
156+
await expect(page.getByRole('cell', { name: 'DISTRESS' })).toBeVisible();
157+
await expect(page.getByRole('cell', { name: 'CRITICAL' })).toBeVisible();
158+
await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible();
115159

116160
// save and reload and make sure all columns are still visible
117161
await page.locator('button[title="Save"]').click();
@@ -120,6 +164,56 @@ test.describe('Testing LAD table configuration', () => {
120164
await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeVisible();
121165
await expect(page.getByRole('cell', { name: 'Units' })).toBeVisible();
122166
await expect(page.getByRole('cell', { name: 'Type' })).toBeVisible();
167+
await expect(page.getByRole('cell', { name: 'WATCH' })).toBeVisible();
168+
await expect(page.getByRole('cell', { name: 'WARNING' })).toBeVisible();
169+
await expect(page.getByRole('cell', { name: 'DISTRESS' })).toBeVisible();
170+
await expect(page.getByRole('cell', { name: 'CRITICAL' })).toBeVisible();
171+
await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible();
172+
});
173+
174+
test('When adding something without Units, do not show Units column', async ({ page }) => {
175+
// Create Sine Wave Generator
176+
await createDomainObjectWithDefaults(page, {
177+
type: 'Event Message Generator',
178+
parent: ladTable.uuid
179+
});
180+
181+
await page.goto(ladTable.url);
182+
183+
// Edit LAD table
184+
await page.getByLabel('Edit').click();
185+
await page.getByRole('tab', { name: 'LAD Table Configuration' }).click();
186+
187+
// make sure Sine Wave headers are visible initially too
188+
await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeVisible();
189+
await expect(page.getByRole('cell', { name: 'Units' })).toBeVisible();
190+
await expect(page.getByRole('cell', { name: 'Type' })).toBeVisible();
191+
await expect(page.getByRole('cell', { name: 'WATCH' })).toBeVisible();
192+
await expect(page.getByRole('cell', { name: 'WARNING' })).toBeVisible();
193+
await expect(page.getByRole('cell', { name: 'DISTRESS' })).toBeVisible();
194+
await expect(page.getByRole('cell', { name: 'CRITICAL' })).toBeVisible();
195+
await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible();
196+
197+
// save and reload and verify they columns are still hidden
198+
await page.getByLabel('Save').click();
199+
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
200+
201+
// Remove Sin Wave Generator
202+
openObjectTreeContextMenu(page, sineWaveObject.url);
203+
await page.getByRole('menuitem', { name: /Remove/ }).click();
204+
await page.getByRole('button', { name: 'OK' }).click();
205+
206+
// Ensure Units & Limit columns are gone
207+
// as Event Generator don't have them
208+
await page.goto(ladTable.url);
209+
await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeVisible();
210+
await expect(page.getByRole('cell', { name: 'Type' })).toBeVisible();
211+
await expect(page.getByRole('cell', { name: 'Units' })).toBeHidden();
212+
await expect(page.getByRole('cell', { name: 'WATCH' })).toBeHidden();
213+
await expect(page.getByRole('cell', { name: 'WARNING' })).toBeHidden();
214+
await expect(page.getByRole('cell', { name: 'DISTRESS' })).toBeHidden();
215+
await expect(page.getByRole('cell', { name: 'CRITICAL' })).toBeHidden();
216+
await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeHidden();
123217
});
124218

125219
test("LAD Tables don't allow selection of rows but does show context click menus", async ({

package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"@percy/playwright": "1.0.4",
1111
"@playwright/test": "1.39.0",
1212
"@types/eventemitter3": "1.2.0",
13-
"@types/jasmine": "4.3.4",
13+
"@types/jasmine": "5.1.2",
1414
"@types/lodash": "4.14.192",
1515
"@vue/compiler-sfc": "3.3.8",
1616
"babel-loader": "9.1.0",
@@ -30,7 +30,7 @@
3030
"eslint-plugin-playwright": "0.12.0",
3131
"eslint-plugin-prettier": "4.2.1",
3232
"eslint-plugin-simple-import-sort": "10.0.0",
33-
"eslint-plugin-unicorn": "48.0.1",
33+
"eslint-plugin-unicorn": "49.0.0",
3434
"eslint-plugin-vue": "9.18.1",
3535
"eslint-plugin-you-dont-need-lodash-underscore": "6.13.0",
3636
"eventemitter3": "1.2.0",
@@ -76,7 +76,7 @@
7676
"vue": "3.3.8",
7777
"vue-eslint-parser": "9.3.2",
7878
"vue-loader": "16.8.3",
79-
"webpack": "5.88.0",
79+
"webpack": "5.89.0",
8080
"webpack-cli": "5.1.1",
8181
"webpack-dev-server": "4.15.1",
8282
"webpack-merge": "5.10.0"

0 commit comments

Comments
 (0)