Skip to content

Commit 4dc4890

Browse files
committed
test
1 parent 42c8900 commit 4dc4890

File tree

8 files changed

+279
-79
lines changed

8 files changed

+279
-79
lines changed

buildspec/linuxE2ETests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ phases:
3737
commands:
3838
- export HOME=/home/codebuild-user
3939
# Ignore failure until throttling issues are fixed.
40-
- xvfb-run npm run testE2E; npm run mergeReports -- "$?"
40+
- xvfb-run npm run testE2E; npm run createTestReport -- "$?"
4141
- VCS_COMMIT_ID="${CODEBUILD_RESOLVED_SOURCE_VERSION}"
4242
- CI_BUILD_URL=$(echo $CODEBUILD_BUILD_URL | sed 's/#/%23/g')
4343
- CI_BUILD_ID="${CODEBUILD_BUILD_ID}"

buildspec/linuxIntegrationTests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ phases:
9292
build:
9393
commands:
9494
- export HOME=/home/codebuild-user
95-
- xvfb-run npm run testInteg; npm run mergeReports -- "$?"
95+
- xvfb-run npm run testInteg; npm run createTestReport -- "$?"
9696
- VCS_COMMIT_ID="${CODEBUILD_RESOLVED_SOURCE_VERSION}"
9797
- CI_BUILD_URL=$(echo $CODEBUILD_BUILD_URL | sed 's/#/%23/g')
9898
- CI_BUILD_ID="${CODEBUILD_BUILD_ID}"

buildspec/linuxTests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ phases:
4141
# Ensure that "foo | run_and_report" fails correctly.
4242
set -o pipefail
4343
. buildspec/shared/common.sh
44-
{ 2>&1 xvfb-run npm test --silent; npm run mergeReports -- "$?"; } | run_and_report 2 \
44+
{ 2>&1 xvfb-run npm test --silent; npm run createTestReport -- "$?"; } | run_and_report 2 \
4545
'rejected promise not handled' \
4646
'This typically indicates a bug. Read https://developer.mozilla.org/docs/Web/JavaScript/Guide/Using_promises#error_handling'
4747
}

package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@
2525
"buildCustomLintPlugin": "npm run build -w plugins/eslint-plugin-aws-toolkits",
2626
"compile": "npm run compile -w packages/",
2727
"testCompile": "npm run testCompile -w packages/ --if-present",
28-
"test": "npm run test -w packages/ --if-present",
29-
"testWeb": "npm run testWeb -w packages/ --if-present",
30-
"testE2E": "npm run testE2E -w packages/ --if-present",
31-
"testInteg": "npm run testInteg -w packages/ --if-present",
28+
"test": "npm run test -w packages/ --if-present; npm run createTestReport",
29+
"testWeb": "npm run testWeb -w packages/ --if-present; npm run createTestReport",
30+
"testE2E": "npm run testE2E -w packages/ --if-present; npm run createTestReport",
31+
"testInteg": "npm run testInteg -w packages/ --if-present; npm run createTestReport",
3232
"package": "npm run package -w packages/toolkit -w packages/amazonq",
3333
"newChange": "echo 'Must specify subproject/workspace with -w packages/<subproject>' && false",
3434
"createRelease": "echo 'Must specify subproject/workspace with -w packages/<subproject>' && false",
@@ -37,7 +37,7 @@
3737
"clean": "npm run clean -w packages/ -w plugins/",
3838
"reset": "npm run clean && ts-node ./scripts/clean.ts node_modules && npm install",
3939
"generateNonCodeFiles": "npm run generateNonCodeFiles -w packages/ --if-present",
40-
"mergeReports": "ts-node ./scripts/mergeReports.ts"
40+
"createTestReport": "ts-node ./scripts/createTestReport.ts"
4141
},
4242
"devDependencies": {
4343
"@aws-toolkits/telemetry": "^1.0.289",

packages/amazonq/test/unit/validation.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,32 @@ describe('package validations', function () {
2727
)
2828
assert.deepStrictEqual(packageJson.contributes.icons, corePackageJson.contributes.icons)
2929
})
30+
31+
describe('foo', () => {
32+
it('bar1', () => {
33+
assert.ok(true)
34+
})
35+
describe('fi', () => {
36+
it('bar2', () => {
37+
assert.ok(true)
38+
})
39+
describe('fo234', () => {
40+
it('bar3', () => {
41+
throw new Error('foo')
42+
})
43+
})
44+
})
45+
})
46+
47+
describe('package validations', () => {
48+
it('wee', () => {
49+
assert.ok(true)
50+
})
51+
52+
describe('foo', () => {
53+
it('wee 2', () => {
54+
assert.ok(true)
55+
})
56+
})
57+
})
3058
})

packages/core/src/test/shared/applicationBuilder/explorer/nodes/appNode.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ describe('AppNode', () => {
169169

170170
describe('getTreeItem', () => {
171171
it('should return a TreeItem with the correct properties', () => {
172+
assert.fail('failed')
172173
const treeItem = appNode.getTreeItem()
173174
const expextedLabel = path.join('VSCode Example Workspace', 'Project One Root Folder')
174175

scripts/createTestReport.ts

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import * as fs from 'fs'
7+
import * as xml2js from 'xml2js'
8+
9+
interface TestFailure {
10+
$: {
11+
message: string
12+
}
13+
_: string
14+
}
15+
16+
interface TestCase {
17+
$: {
18+
classname: string
19+
name: string
20+
time: string
21+
}
22+
failure?: TestFailure[]
23+
}
24+
25+
interface TestSuite {
26+
$: {
27+
name: string
28+
tests: string
29+
failures: string
30+
errors: string
31+
time: string
32+
file: string
33+
}
34+
testcase: TestCase[] | undefined
35+
}
36+
37+
interface TestReport {
38+
testsuites: {
39+
testsuite: TestSuite[]
40+
}
41+
}
42+
43+
interface TestSummary {
44+
totalTests: number
45+
totalFailures: number
46+
totalTime: number
47+
failedTests: FailedTest[]
48+
}
49+
50+
interface FailedTest {
51+
suite: string
52+
test: string
53+
message: string
54+
contents: string
55+
path: string[]
56+
}
57+
58+
/**
59+
* Merge all of the packages/ test reports into a single directory
60+
*/
61+
async function createTestReport() {
62+
console.log('Merging test reports')
63+
64+
const packagesDir = `${__dirname}/../packages`
65+
66+
// Get all packages/* directories
67+
const packageDirs = fs.readdirSync(packagesDir).map((dir) => `${packagesDir}/${dir}`)
68+
69+
// Find report.xml files in .test-reports subdirectories
70+
const testReports = packageDirs
71+
.map((dir) => `${dir}/.test-reports/report.xml`)
72+
.filter((file) => fs.existsSync(file))
73+
74+
const mergedReport: TestReport = {
75+
testsuites: {
76+
testsuite: [],
77+
},
78+
}
79+
80+
const failedTests: FailedTest[] = []
81+
let totalTests = 0
82+
let totalFailures = 0
83+
let totalTime = 0
84+
85+
let filePath = ''
86+
let suites = new Set<string>()
87+
88+
/**
89+
* Collect all test reports into a single merged test report object.
90+
* Also keeps track of test count, test failures, and test run time
91+
*/
92+
for (const file of testReports) {
93+
const content = fs.readFileSync(file)
94+
const result: { testsuites: { testsuite: TestSuite[] } } = await xml2js.parseStringPromise(content)
95+
if (result.testsuites && result.testsuites.testsuite) {
96+
for (const suite of result.testsuites.testsuite) {
97+
if (suite.$.file !== filePath) {
98+
filePath = suite.$.file
99+
suites = new Set<string>()
100+
}
101+
102+
for (const testcase of suite.testcase ?? []) {
103+
if (testcase.failure) {
104+
const testPath = parseTestHierarchy(suites, testcase.$.classname, suite.$.name, testcase.$.name)
105+
failedTests.push({
106+
suite: suite.$.name,
107+
test: testcase.$.name,
108+
message: testcase.failure[0].$.message,
109+
contents: testcase.failure[0]._,
110+
path: testPath,
111+
})
112+
}
113+
}
114+
115+
totalTests += parseInt(suite.$.tests, 10)
116+
totalFailures += parseInt(suite.$.failures, 10)
117+
totalTime += parseFloat(suite.$.time)
118+
119+
suites.add(suite.$.name)
120+
}
121+
122+
mergedReport.testsuites.testsuite.push(...result.testsuites.testsuite)
123+
}
124+
}
125+
126+
printTestSummary({
127+
totalTests,
128+
totalFailures,
129+
totalTime,
130+
failedTests,
131+
})
132+
133+
writeReport(mergedReport)
134+
}
135+
136+
/**
137+
* Extracts and constructs a hierarchical test path from a test case identifier
138+
*
139+
* @param suites - Set of known test suite names
140+
* @param className - Name of the test class
141+
* @param suiteName - Name of the test suite
142+
* @param testcaseName - Full name of the test case
143+
* @example
144+
* parseTestHierarchy(new Set(["package validations"]), 'bar1', 'foo', 'package validations foo bar1') -> ["package validations", "bar1", "foo"]
145+
* @returns An array of path components representing the test hierarchy
146+
*/
147+
function parseTestHierarchy(suites: Set<string>, className: string, suiteName: string, testcaseName: string) {
148+
let remainingPath = testcaseName
149+
remainingPath = remainingPath.substring(0, remainingPath.lastIndexOf(className))
150+
remainingPath = remainingPath.substring(0, remainingPath.lastIndexOf(suiteName))
151+
152+
const pathComponents = remainingPath.trim().split(' ')
153+
let index = 0
154+
let currentComponent = pathComponents[0]
155+
const path = []
156+
while (remainingPath.length > 0) {
157+
index++
158+
if (!suites.has(currentComponent)) {
159+
currentComponent = currentComponent + ' ' + pathComponents[index]
160+
} else {
161+
path.push(currentComponent)
162+
remainingPath = remainingPath.substring(currentComponent.length).trim()
163+
currentComponent = pathComponents[index]
164+
}
165+
}
166+
167+
path.push(suiteName)
168+
path.push(className)
169+
170+
return path
171+
}
172+
173+
function printTestSummary({ totalTests, totalFailures, totalTime, failedTests }: TestSummary) {
174+
const passingTests = totalTests - totalFailures
175+
const pendingTests = 0
176+
177+
console.log(`${passingTests} passing (${Math.round(totalTime)}s)`)
178+
if (pendingTests > 0) {
179+
console.log(`${pendingTests} pending`)
180+
}
181+
if (totalFailures > 0) {
182+
console.log(`${totalFailures} failing`)
183+
184+
failedTests.forEach((test, index) => {
185+
let indent = ' '
186+
187+
for (let x = 0; x < test.path.length; x++) {
188+
if (x == 0) {
189+
console.log(`${indent}${index + 1}) ${test.path[x]}`)
190+
indent += ' '
191+
} else {
192+
console.log(`${indent}${test.path[x]}`)
193+
}
194+
indent += ' '
195+
}
196+
197+
if (test.contents) {
198+
// Indent the stack trace
199+
console.log(
200+
test.contents
201+
.split('\n')
202+
.map((line) => `${indent}${line}`)
203+
.join('\n')
204+
)
205+
}
206+
console.log() // Add empty line between failures
207+
})
208+
}
209+
}
210+
211+
function writeReport(mergedReport: TestReport) {
212+
const builder = new xml2js.Builder()
213+
const xml = builder.buildObject(mergedReport)
214+
215+
/**
216+
* Create the new test reports directory and write the test report
217+
*/
218+
const reportsDir = `${__dirname}/../.test-reports`
219+
220+
// Create reports directory if it doesn't exist
221+
if (!fs.existsSync(reportsDir)) {
222+
fs.mkdirSync(reportsDir, { recursive: true })
223+
}
224+
225+
fs.writeFileSync(`${reportsDir}/report.xml`, xml)
226+
227+
const exitCodeArg = process.argv[2]
228+
if (exitCodeArg) {
229+
/**
230+
* Retrieves the exit code from the previous test run execution.
231+
*
232+
* This allows us to:
233+
* 1. Merge and upload test reports regardless of the test execution status
234+
* 2. Preserve the original test run exit code
235+
* 3. Report the test status back to CI
236+
*/
237+
const exitCode = parseInt(exitCodeArg || '0', 10)
238+
process.exit(exitCode)
239+
}
240+
}
241+
242+
createTestReport()

0 commit comments

Comments
 (0)