Skip to content

Commit 0865f53

Browse files
authored
feat: Update testing (#538)
Updates test to consolidate all functionality, and only test changed projects.
1 parent 34d7b6e commit 0865f53

File tree

1 file changed

+102
-35
lines changed

1 file changed

+102
-35
lines changed

e2e/samples.spec.ts

Lines changed: 102 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,78 @@
1717
import { test, expect } from '@playwright/test';
1818
import fs from 'fs';
1919
import path from 'path';
20-
import childProcess from 'child_process';
20+
import childProcess, { execSync } from 'child_process';
2121

2222
const samplesDir = path.join(__dirname, '..', 'samples');
2323

24-
const sampleFolders = fs.readdirSync(samplesDir).filter((file) => {
25-
return fs.statSync(path.join(samplesDir, file)).isDirectory();
26-
});
24+
// Function to return all sample folders.
25+
const getAllSampleFolders = () => {
26+
return fs.readdirSync(samplesDir).filter((file) => {
27+
// Ensure we are only looking at directories within samplesDir, excluding find-changes.sh itself if it's a file
28+
const filePath = path.join(samplesDir, file);
29+
return fs.statSync(filePath).isDirectory();
30+
});
31+
};
32+
33+
// Function to return only changed sample folders.
34+
const getChangedSampleFolders = (): string[] => {
35+
try {
36+
const scriptPath = path.join(__dirname, '..', 'samples', 'find-changes.sh');
37+
38+
if (!fs.existsSync(scriptPath)) {
39+
console.warn(`Warning: find-changes.sh not found at ${scriptPath}. Running tests for all samples.`);
40+
return getAllSampleFolders();
41+
}
42+
43+
// Execute the script from the project root.
44+
const projectRoot = path.join(__dirname, '..');
45+
const output = execSync(`sh ${scriptPath}`, { cwd: projectRoot, encoding: 'utf-8' });
46+
47+
// Get all folder names outputted by the script
48+
const rawChangedFolders = output.trim().split('\n');
49+
// Filter out empty strings that might result from multiple newlines or a trailing newline
50+
const changedFolders = rawChangedFolders.filter(folder => folder.trim().length > 0);
51+
52+
if (changedFolders.length === 0) {
53+
// This means find-changes.sh ran successfully and reported no changes.
54+
console.log("find-changes.sh reported no changed folders. Skipping tests.");
55+
return [];
56+
}
57+
58+
// Validate that changed folders actually exist in samplesDir
59+
const validChangedFolders = changedFolders.filter(folderName => {
60+
const folderPath = path.join(samplesDir, folderName);
61+
return fs.existsSync(folderPath) && fs.statSync(folderPath).isDirectory();
62+
});
63+
64+
if (validChangedFolders.length === 0 && changedFolders.length > 0 && changedFolders[0] !== "") {
65+
console.warn("find-changes.sh outputted folder names, but none are valid sample directories. Running for all samples.");
66+
return [];
67+
}
68+
69+
console.log("Running tests only for changed samples: ", validChangedFolders);
70+
return validChangedFolders;
71+
72+
} catch (error) {
73+
console.error("Error running find-changes.sh, falling back to all samples:", error);
74+
return getAllSampleFolders();
75+
}
76+
};
77+
78+
const foldersToTest = getChangedSampleFolders();
79+
80+
if (foldersToTest.length === 0) {
81+
console.log("No sample folders identified to test. This might indicate no changes or an issue with find-changes.sh.");
82+
} else {
83+
console.log(`Will run tests for the following folders: ${foldersToTest.join(', ')}`);
84+
}
2785

2886
// Iterate through samples and run the same test for each one.
29-
sampleFolders.forEach((sampleFolder) => {
87+
foldersToTest.forEach((sampleFolder) => {
3088
test(`test ${sampleFolder}`, async ({ page }) => {
3189

3290
// START Build the sample
33-
const buildProcess = childProcess.spawn('npm', ['run', 'test'], {
91+
const buildProcess = childProcess.spawn('npm', ['run', 'build'], {
3492
cwd: path.join(samplesDir, sampleFolder),
3593
stdio: 'inherit',
3694
});
@@ -40,24 +98,28 @@ sampleFolders.forEach((sampleFolder) => {
4098
if (code === 0) {
4199
resolve(true);
42100
} else {
43-
reject(`Build process exited with code ${code}`);
101+
reject(new Error(`Build process for ${sampleFolder} exited with code ${code}`));
44102
}
45103
});
104+
buildProcess.on('error', (err) => {
105+
reject(new Error(`Failed to start build process for ${sampleFolder}: ${err.message}`));
106+
});
46107
});
47108
// END Build the sample
48109

49110
// START run the preview
50111
// Get an available port
51112
const port = 8080;
52-
53113
const url = `http://localhost:${port}/`;
54114

55115
const viteProcess = childProcess.spawn('vite', ['preview', `--port=${port}`], {
56116
cwd: path.join(samplesDir, sampleFolder),
57-
stdio: 'inherit',
117+
stdio: 'inherit',
118+
detached: true, // Allows parent to exit independently, though we kill it in finally
58119
});
59120

60-
await new Promise((resolve) => setTimeout(resolve, 500)); // Set a timeout to let the web server start.
121+
//await new Promise((resolve) => setTimeout(resolve, 500)); // Set a timeout to let the web server start.
122+
await page.waitForTimeout(500);
61123
// END run the preview
62124

63125
/**
@@ -66,32 +128,24 @@ sampleFolders.forEach((sampleFolder) => {
66128
* Run `npx playwright test --ui` to launch Playwright in UI mode to iteratively debug this file.
67129
*/
68130
try {
69-
// Check for console errors. Define a promise, then call after page load.
70131
const consoleErrors: string[] = [];
71-
const errorPromise = new Promise<void>((resolve) => {
72-
page.on('console', (msg => {
73-
if (msg.type() === 'error') {
74-
consoleErrors.push(msg.text());
75-
resolve();
76-
}
77-
}));
78-
79-
page.on('pageerror', (exception) => {
80-
consoleErrors.push(exception.message);
81-
resolve();
82-
});
83-
84-
// Set a timeout to resolve the promise even if no error occurs.
85-
setTimeout(() => {
86-
resolve();
87-
}, 500);
132+
// Capture console errors and page errors
133+
page.on('console', (msg) => {
134+
if (msg.type() === 'error') {
135+
consoleErrors.push(msg.text());
136+
}
137+
});
138+
page.on('pageerror', (exception) => {
139+
consoleErrors.push(exception.message);
88140
});
89141

90142
// Navigate to the page.
143+
//await page.goto(url, { waitUntil: 'networkidle', timeout: 500 });
91144
await page.goto(url);
92145

93-
// There must be no critical console errors.
94-
await errorPromise;
146+
// Allow some time for async operations and errors to be caught
147+
await page.waitForTimeout(500);
148+
95149
// Filter out error messages we can safely avoid.
96150
const filteredErrorMessages = [
97151
'Falling back to Raster',
@@ -100,23 +154,26 @@ sampleFolders.forEach((sampleFolder) => {
100154
const criticalErrors = consoleErrors.filter(error =>
101155
!filteredErrorMessages.some(message => error.includes(message))
102156
);
157+
158+
if (criticalErrors.length > 0) {
159+
console.error(`Critical console errors found in ${sampleFolder}:`, criticalErrors);
160+
}
103161
expect(criticalErrors).toHaveLength(0);
104162

105163
// Wait for the page DOM to load; this does NOT include the Google Maps APIs.
106-
await page.waitForLoadState('domcontentloaded');
164+
await page.waitForLoadState('domcontentloaded', { timeout: 500 });
107165

108166
// Wait for Google Maps to load.
109-
await page.waitForFunction(() => window.google && window.google.maps);
167+
await page.waitForFunction(() => window.google && window.google.maps, { timeout: 500 });
110168

111169
// Insert a delay in ms to let the map load.
112-
await new Promise((resolve) => setTimeout(resolve, 1000));
170+
await page.waitForTimeout(500);
113171

114172
// Assertions. These must be met or the test will fail.
115173
// The sample must load the Google Maps API.
116174
const hasGoogleMaps = await page.evaluate(() => {
117175
return typeof window.google !== 'undefined' && typeof window.google.maps !== 'undefined';
118176
});
119-
120177
await expect(hasGoogleMaps).toBeTruthy();
121178

122179
/**const mapElement = await page.locator('#map');
@@ -127,7 +184,17 @@ sampleFolders.forEach((sampleFolder) => {
127184
throw new Error('Assertion failed: Map is not visible.');
128185
}*/
129186
} finally {
130-
viteProcess.kill();
187+
//viteProcess.kill(); // We used to just kill the process. Curious to see about how the other stuff works.
188+
if (viteProcess.pid) {
189+
try {
190+
// Use process.kill for cross-platform compatibility, sending SIGINT
191+
process.kill(viteProcess.pid, 'SIGINT');
192+
} catch (e) {
193+
console.warn(`Failed to kill Vite process for ${sampleFolder} (PID: ${viteProcess.pid}):`, e.message);
194+
}
195+
}
196+
// Add a small delay to allow the process to terminate
197+
await page.waitForTimeout(500);
131198
}
132199
});
133200
});

0 commit comments

Comments
 (0)