Skip to content

Commit af4a01c

Browse files
authored
Merge pull request #3237 from SalesforceCommerceCloud/hotfix/validate-project-type-err
[E2E][Bugfix] Fix usage for Object.hasOwn
2 parents 2c5ba77 + c0f4de8 commit af4a01c

File tree

4 files changed

+169
-6
lines changed

4 files changed

+169
-6
lines changed

.github/workflows/e2e-pr.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ jobs:
106106
- name: Create .env with environment variables to be set on the MRT target
107107
run: |-
108108
echo "PWA_KIT_SLAS_CLIENT_SECRET=${{ secrets.ZZRF_002_SLAS_PRIVATE_CLIENT_SECRET }}" > ${{ vars.MRT_ENV_VARS_E2E_BASE_FILENAME }}
109+
echo "OTEL_TRACING_ENABLED=true" >> ${{ vars.MRT_ENV_VARS_E2E_BASE_FILENAME }}
109110
110111
# Call the e2e/scripts/update-mrt-target.js script to update the MRT target config and environment variables.
111112
# This script is a Node.js script that makes a PATCH request to the MRT API to update the MRT target config and environment variables.

e2e/scripts/validate-generated-project.js

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ const validateExtensibilityConfig = async (project, templateVersion) => {
4545
const pkg = require(pkgPath)
4646
return new Promise((resolve, reject) => {
4747
if (
48-
!pkg.hasOwn('ccExtensibility') ||
49-
!pkg['ccExtensibility'].hasOwn('extends') ||
50-
!pkg['ccExtensibility'].hasOwn('overridesDir') ||
48+
!Object.hasOwn(pkg, 'ccExtensibility') ||
49+
!Object.hasOwn(pkg['ccExtensibility'], 'extends') ||
50+
!Object.hasOwn(pkg['ccExtensibility'], 'overridesDir') ||
5151
pkg['ccExtensibility'].extends !== '@salesforce/retail-react-app' ||
5252
pkg['ccExtensibility'].overridesDir !== 'overrides'
5353
) {
@@ -94,6 +94,15 @@ program
9494
)
9595
.option('--templateVersion <templateVersion>', 'Template version used to generate the project')
9696

97-
program.parse(process.argv)
97+
// Export functions for testing
98+
module.exports = {
99+
validateGeneratedArtifacts,
100+
validateExtensibilityConfig,
101+
main
102+
}
98103

99-
main(program)
104+
// Only run CLI when file is executed directly
105+
if (require.main === module) {
106+
program.parse(process.argv)
107+
main(program)
108+
}
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/*
2+
* Copyright (c) 2025, Salesforce, Inc.
3+
* All rights reserved.
4+
* SPDX-License-Identifier: BSD-3-Clause
5+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6+
*/
7+
8+
const fs = require('fs')
9+
const path = require('path')
10+
11+
// Mock the dependencies
12+
jest.mock('fs')
13+
14+
// Mocking the config.js file to allow testing with smaller arrays of expected artifacts
15+
jest.mock('../config.js', () => ({
16+
GENERATED_PROJECTS_DIR: '../generated-projects',
17+
EXPECTED_GENERATED_ARTIFACTS: {
18+
'retail-app-demo': ['package.json', 'node_modules', 'config'],
19+
'retail-app-ext': ['package.json', 'node_modules', 'overrides']
20+
}
21+
}))
22+
jest.mock('./utils.js', () => ({
23+
diffArrays: jest.fn()
24+
}))
25+
26+
// Import the functions to test
27+
const {diffArrays} = require('./utils.js')
28+
const {validateGeneratedArtifacts} = require('./validate-generated-project.js')
29+
30+
describe('validateGeneratedArtifacts', () => {
31+
beforeEach(() => {
32+
jest.clearAllMocks()
33+
})
34+
35+
test('resolves when all expected artifacts are present', async () => {
36+
const project = 'retail-app-demo'
37+
const expectedArtifacts = ['package.json', 'node_modules', 'config']
38+
const actualArtifacts = ['package.json', 'node_modules', 'config', 'extra-file']
39+
40+
fs.readdirSync.mockReturnValue(actualArtifacts)
41+
diffArrays.mockReturnValue([])
42+
43+
const result = await validateGeneratedArtifacts(project)
44+
45+
expect(fs.readdirSync).toHaveBeenCalledWith(
46+
// path.sep is used to handle the platform-specific path separator. (Windows uses \ and other platforms use /)
47+
expect.stringContaining(`generated-projects${path.sep}${project}`)
48+
)
49+
expect(diffArrays).toHaveBeenCalledWith(expectedArtifacts, actualArtifacts)
50+
expect(result).toBe(`Successfully validated generated artifacts for: ${project} `)
51+
})
52+
53+
test('rejects when artifacts are missing', async () => {
54+
const project = 'retail-app-demo'
55+
const actualArtifacts = ['package.json', 'node_modules']
56+
const missingArtifacts = ['config']
57+
58+
fs.readdirSync.mockReturnValue(actualArtifacts)
59+
diffArrays.mockReturnValue(missingArtifacts)
60+
61+
await expect(validateGeneratedArtifacts(project)).rejects.toBe(
62+
`Generated project (${project}) is missing one or more artifacts: ${missingArtifacts}`
63+
)
64+
})
65+
66+
test('rejects when project directory does not exist', async () => {
67+
const project = 'non-existent-project'
68+
const error = new Error('ENOENT: no such file or directory')
69+
70+
fs.readdirSync.mockImplementation(() => {
71+
throw error
72+
})
73+
74+
await expect(validateGeneratedArtifacts(project)).rejects.toBe(
75+
`Generated project (${project}) is missing one or more artifacts: ${error}`
76+
)
77+
})
78+
79+
test('handles project with no expected artifacts', async () => {
80+
const project = 'unknown-project'
81+
const actualArtifacts = ['some-file']
82+
83+
fs.readdirSync.mockReturnValue(actualArtifacts)
84+
diffArrays.mockReturnValue([])
85+
86+
const result = await validateGeneratedArtifacts(project)
87+
88+
expect(diffArrays).toHaveBeenCalledWith([], actualArtifacts)
89+
expect(result).toBe(`Successfully validated generated artifacts for: ${project} `)
90+
})
91+
})
92+
93+
// Since it requires files at runtime, we'll test the key validation logic
94+
describe('validateExtensibilityConfig validation logic', () => {
95+
test('validates Object.hasOwn usage for extensibility config', () => {
96+
// Test the core validation logic that was fixed
97+
const validConfig = {
98+
ccExtensibility: {
99+
extends: '@salesforce/retail-react-app',
100+
overridesDir: 'overrides'
101+
}
102+
}
103+
104+
const invalidConfigMissingProperty = {
105+
ccExtensibility: {
106+
extends: '@salesforce/retail-react-app'
107+
// missing overridesDir
108+
}
109+
}
110+
111+
const invalidConfigWrongExtends = {
112+
ccExtensibility: {
113+
extends: '@wrong/package',
114+
overridesDir: 'overrides'
115+
}
116+
}
117+
118+
expect(Object.hasOwn(validConfig, 'ccExtensibility')).toBe(true)
119+
expect(Object.hasOwn(validConfig.ccExtensibility, 'extends')).toBe(true)
120+
expect(Object.hasOwn(validConfig.ccExtensibility, 'overridesDir')).toBe(true)
121+
122+
expect(Object.hasOwn(invalidConfigMissingProperty.ccExtensibility, 'overridesDir')).toBe(
123+
false
124+
)
125+
126+
const isValidConfig = (pkg) => {
127+
return (
128+
Object.hasOwn(pkg, 'ccExtensibility') &&
129+
Object.hasOwn(pkg.ccExtensibility, 'extends') &&
130+
Object.hasOwn(pkg.ccExtensibility, 'overridesDir') &&
131+
pkg.ccExtensibility.extends === '@salesforce/retail-react-app' &&
132+
pkg.ccExtensibility.overridesDir === 'overrides'
133+
)
134+
}
135+
136+
expect(isValidConfig(validConfig)).toBe(true)
137+
expect(isValidConfig(invalidConfigMissingProperty)).toBe(false)
138+
expect(isValidConfig(invalidConfigWrongExtends)).toBe(false)
139+
})
140+
141+
test('validates template version matching logic', () => {
142+
const pkg = {version: '1.0.0'}
143+
144+
const validateVersion = (pkg, templateVersion) => {
145+
return !templateVersion || pkg.version === templateVersion
146+
}
147+
148+
expect(validateVersion(pkg, undefined)).toBe(true)
149+
expect(validateVersion(pkg, null)).toBe(true)
150+
expect(validateVersion(pkg, '1.0.0')).toBe(true)
151+
expect(validateVersion(pkg, '2.0.0')).toBe(false)
152+
})
153+
})

e2e/tests/opentelemetry-b3-tracing.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ test.beforeEach(async ({page}) => {
1616
})
1717
})
1818

19-
test.skip('should inject B3 headers when __server_timing param is passed', async ({page}) => {
19+
test('should inject B3 headers when __server_timing param is passed', async ({page}) => {
2020
const url = `${config.RETAIL_APP_HOME}?__server_timing=true`
2121

2222
const responseHeaders = []

0 commit comments

Comments
 (0)