Compare commits

...

1 Commits

Author SHA1 Message Date
Jade Welton
9f7639f86e Code coverage pre-QA push. 2026-03-06 11:16:12 -08:00
17 changed files with 1234 additions and 217 deletions

View File

@@ -161,6 +161,7 @@ jobs:
env:
NODE_TLS_REJECT_UNAUTHORIZED: 0
AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
COVERAGE: "true"
strategy:
fail-fast: false
matrix:
@@ -219,6 +220,13 @@ jobs:
name: blob-report-${{ matrix.shardIndex }}
path: blob-report
retention-days: 1
- name: Upload coverage data
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v4
with:
name: coverage-data-${{ matrix.shardIndex }}
path: .nyc_output
retention-days: 1
merge-playwright-reports:
name: "Merge Playwright Reports"
@@ -251,3 +259,39 @@ jobs:
name: html-report--attempt-${{ github.run_attempt }}
path: playwright-report
retention-days: 14
merge-coverage:
name: "Merge Code Coverage"
if: ${{ !cancelled() }}
needs: [playwright-tests]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
- name: Install dependencies
run: npm ci
- name: Download coverage data from all shards
uses: actions/download-artifact@v4
with:
path: all-coverage
pattern: coverage-data-*
merge-multiple: true
- name: Merge coverage data
run: |
mkdir -p .nyc_output
cp all-coverage/*.json .nyc_output/ 2>/dev/null || true
npx nyc merge .nyc_output .nyc_output/merged-coverage.json
- name: Generate coverage report
run: npx nyc report --temp-dir .nyc_output --report-dir coverage-e2e --reporter text --reporter text-summary --reporter lcov --reporter html
- name: Upload coverage report
uses: actions/upload-artifact@v4
with:
name: coverage-report--attempt-${{ github.run_attempt }}
path: coverage-e2e
retention-days: 14

2
.gitignore vendored
View File

@@ -1,6 +1,8 @@
built/*
dist/*
coverage/**
coverage-e2e/**
.nyc_output/
css/*
Definitions/**/*
libs/**/*

16
.nycrc.json Normal file
View File

@@ -0,0 +1,16 @@
{
"extends": "@istanbuljs/nyc-config-typescript",
"all": true,
"include": ["src/**/*.ts", "src/**/*.tsx"],
"exclude": [
"src/**/*.test.ts",
"src/**/*.test.tsx",
"src/**/*.spec.ts",
"src/**/*.spec.tsx",
"src/**/*.d.ts",
"src/setupTests.ts"
],
"reporter": ["text", "text-summary", "lcov", "html"],
"report-dir": "coverage-e2e",
"temp-dir": ".nyc_output"
}

1267
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -152,6 +152,7 @@
"@webpack-cli/serve": "2.0.5",
"babel-jest": "29.7.0",
"babel-loader": "8.1.0",
"babel-plugin-istanbul": "7.0.1",
"buffer": "5.1.0",
"case-sensitive-paths-webpack-plugin": "2.4.0",
"create-file-webpack": "1.0.2",
@@ -182,6 +183,7 @@
"mini-css-extract-plugin": "2.1.0",
"monaco-editor-webpack-plugin": "7.1.0",
"node-fetch": "2.6.7",
"nyc": "18.0.0",
"prettier": "3.0.3",
"process": "0.11.10",
"querystring-es3": "0.2.1",
@@ -213,6 +215,9 @@
"test": "rimraf coverage && jest",
"test:debug": "jest --runInBand",
"test:e2e": "jest -c ./jest.config.playwright.js --detectOpenHandles",
"test:e2e:coverage": "COVERAGE=true npx playwright test",
"coverage:merge": "nyc merge .nyc_output .nyc_output/merged-coverage.json",
"coverage:report": "nyc report --temp-dir .nyc_output --report-dir coverage-e2e --reporter text --reporter text-summary --reporter lcov --reporter html",
"test:file": "jest --coverage=false",
"watch": "npm run start",
"wait-for-server": "wait-on -t 240000 -i 5000 -v https-get://0.0.0.0:1234/",

55
test/baseTest.ts Normal file
View File

@@ -0,0 +1,55 @@
import { test as base, expect, Page, TestInfo } from "@playwright/test";
import * as crypto from "crypto";
import * as fs from "fs";
import * as path from "path";
const NYC_OUTPUT_DIR = path.resolve(__dirname, "../.nyc_output");
/**
* Extended Playwright test that automatically collects Istanbul code coverage
* from all frames after each test. Coverage is only collected when the
* COVERAGE environment variable is set to "true".
*
* Istanbul instrumentation must be enabled in the webpack build (see webpack.config.js).
* The instrumented code exposes `window.__coverage__` which this fixture collects.
*/
export const test = base.extend<{ coverageAutoCollect: void }>({
coverageAutoCollect: [
async ({ page }: { page: Page }, use: () => Promise<void>, testInfo: TestInfo) => {
// Run the test
await use();
// After the test, collect coverage if enabled
if (process.env.COVERAGE !== "true") {
return;
}
// Ensure the output directory exists
if (!fs.existsSync(NYC_OUTPUT_DIR)) {
fs.mkdirSync(NYC_OUTPUT_DIR, { recursive: true });
}
// Collect coverage from all frames (the app runs inside an iframe)
for (const frame of page.frames()) {
try {
const coverage = await frame.evaluate(() => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return (window as any).__coverage__ || null;
});
if (coverage) {
const uniqueId = crypto.randomBytes(8).toString("hex");
const safeName = testInfo.title.replace(/[^a-zA-Z0-9]/g, "_").substring(0, 60);
const filename = `coverage-${safeName}-${uniqueId}.json`;
fs.writeFileSync(path.join(NYC_OUTPUT_DIR, filename), JSON.stringify(coverage));
}
} catch {
// Frame may have been detached or navigated away. That's fine, skip it.
}
}
},
{ auto: true },
],
});
export { expect };

View File

@@ -1,4 +1,4 @@
import { expect, test } from "@playwright/test";
import { expect, test } from "../baseTest";
import { DataExplorer, TestAccount, generateUniqueName } from "../fx";

View File

@@ -1,4 +1,4 @@
import { expect, test } from "@playwright/test";
import { expect, test } from "../baseTest";
import { DataExplorer, TestAccount, generateUniqueName } from "../fx";

View File

@@ -1,4 +1,4 @@
import { expect, test } from "@playwright/test";
import { expect, test } from "../baseTest";
import { DataExplorer, TestAccount, generateUniqueName } from "../fx";

View File

@@ -1,4 +1,4 @@
import { expect, test } from "@playwright/test";
import { expect, test } from "../baseTest";
import { setupCORSBypass } from "../CORSBypass";
import { DataExplorer, DocumentsTab, TestAccount } from "../fx";

View File

@@ -1,4 +1,4 @@
import { expect, test } from "@playwright/test";
import { expect, test } from "../baseTest";
import { DataExplorer, TestAccount, generateUniqueName } from "../fx";

View File

@@ -1,4 +1,4 @@
import { expect, test } from "@playwright/test";
import { expect, test } from "../baseTest";
import { DataExplorer, DocumentsTab, TestAccount } from "../fx";
import { retry, setPartitionKeys } from "../testData";

View File

@@ -1,4 +1,4 @@
import { expect, test } from "@playwright/test";
import { expect, test } from "../baseTest";
import { DataExplorer, Editor, QueryTab, TestAccount } from "../fx";
import { TestContainerContext, TestItem, createTestSQLContainer } from "../testData";

View File

@@ -1,16 +1,16 @@
import { expect, test } from "@playwright/test";
import { expect, test } from "../baseTest";
import { CosmosDBManagementClient } from "@azure/arm-cosmosdb";
import { CosmosClient, PermissionMode } from "@azure/cosmos";
import { AzureIdentityCredentialAdapter } from "@azure/ms-rest-js";
import {
DataExplorer,
TestAccount,
generateUniqueName,
getAccountName,
getAzureCLICredentials,
resourceGroupName,
subscriptionId,
DataExplorer,
TestAccount,
generateUniqueName,
getAccountName,
getAzureCLICredentials,
resourceGroupName,
subscriptionId,
} from "../fx";
test("SQL account using Resource token", async ({ page }) => {

View File

@@ -1,4 +1,4 @@
import { expect, test } from "@playwright/test";
import { expect, test } from "../baseTest";
import { DataExplorer, TestAccount } from "../fx";
test("Self Serve", async ({ page, browserName }) => {

View File

@@ -1,4 +1,4 @@
import { expect, test } from "@playwright/test";
import { expect, test } from "../baseTest";
import { DataExplorer, TestAccount, generateUniqueName } from "../fx";

View File

@@ -65,16 +65,28 @@ const htmlRule = {
};
// We compile our own code with ts-loader
const typescriptLoaders = [
{
loader: "ts-loader",
options: {
transpileOnly: true,
},
},
];
// When COVERAGE is enabled, add babel-loader with istanbul plugin to instrument code
if (process.env.COVERAGE === "true") {
typescriptLoaders.unshift({
loader: "babel-loader",
options: {
plugins: ["istanbul"],
},
});
}
const typescriptRule = {
test: /\.tsx?$/,
use: [
{
loader: "ts-loader",
options: {
transpileOnly: true,
},
},
],
use: typescriptLoaders,
exclude: /node_modules/,
};