rwsdk 0.1.16 → 0.1.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/$.d.mts +8 -0
- package/dist/lib/$.mjs +5 -0
- package/dist/lib/compileTsModule.d.mts +1 -0
- package/dist/lib/compileTsModule.mjs +27 -0
- package/dist/lib/constants.d.mts +4 -0
- package/dist/lib/constants.mjs +6 -0
- package/dist/lib/findWranglerConfig.d.mts +1 -0
- package/dist/lib/findWranglerConfig.mjs +12 -0
- package/dist/lib/getShortName.d.mts +1 -0
- package/dist/lib/getShortName.mjs +2 -0
- package/dist/lib/getSrcPaths.d.ts +15 -0
- package/dist/lib/getSrcPaths.js +80 -0
- package/dist/lib/hasPkgScript.d.mts +1 -0
- package/dist/lib/hasPkgScript.mjs +9 -0
- package/dist/lib/jsonUtils.d.mts +28 -0
- package/dist/lib/jsonUtils.mjs +167 -0
- package/dist/lib/setupEnvFiles.d.mts +4 -0
- package/dist/lib/setupEnvFiles.mjs +31 -0
- package/dist/lib/smokeTests/artifacts.d.mts +10 -0
- package/dist/lib/smokeTests/artifacts.mjs +164 -0
- package/dist/lib/smokeTests/browser.d.mts +48 -0
- package/dist/lib/smokeTests/browser.mjs +1041 -0
- package/dist/lib/smokeTests/cleanup.d.mts +5 -0
- package/dist/lib/smokeTests/cleanup.mjs +214 -0
- package/dist/lib/smokeTests/codeUpdates.d.mts +8 -0
- package/dist/lib/smokeTests/codeUpdates.mjs +229 -0
- package/dist/lib/smokeTests/constants.d.mts +5 -0
- package/dist/lib/smokeTests/constants.mjs +10 -0
- package/dist/lib/smokeTests/development.d.mts +11 -0
- package/dist/lib/smokeTests/development.mjs +209 -0
- package/dist/lib/smokeTests/environment.d.mts +14 -0
- package/dist/lib/smokeTests/environment.mjs +163 -0
- package/dist/lib/smokeTests/release.d.mts +61 -0
- package/dist/lib/smokeTests/release.mjs +526 -0
- package/dist/lib/smokeTests/reporting.d.mts +13 -0
- package/dist/lib/smokeTests/reporting.mjs +355 -0
- package/dist/lib/smokeTests/runSmokeTests.d.mts +5 -0
- package/dist/lib/smokeTests/runSmokeTests.mjs +144 -0
- package/dist/lib/smokeTests/state.d.mts +48 -0
- package/dist/lib/smokeTests/state.mjs +57 -0
- package/dist/lib/smokeTests/templates/SmokeTest.template.d.ts +1 -0
- package/dist/lib/smokeTests/templates/SmokeTest.template.js +81 -0
- package/dist/lib/smokeTests/templates/SmokeTestClient.template.d.ts +1 -0
- package/dist/lib/smokeTests/templates/SmokeTestClient.template.js +159 -0
- package/dist/lib/smokeTests/templates/smokeTestFunctions.template.d.ts +1 -0
- package/dist/lib/smokeTests/templates/smokeTestFunctions.template.js +19 -0
- package/dist/lib/smokeTests/types.d.mts +75 -0
- package/dist/lib/smokeTests/types.mjs +1 -0
- package/dist/lib/smokeTests/utils.d.mts +15 -0
- package/dist/lib/smokeTests/utils.mjs +147 -0
- package/dist/llms/index.d.ts +3 -0
- package/dist/llms/index.js +35 -0
- package/dist/llms/rules/interruptors.d.ts +1 -0
- package/dist/llms/rules/interruptors.js +243 -0
- package/dist/llms/rules/middleware.d.ts +1 -0
- package/dist/llms/rules/middleware.js +71 -0
- package/dist/llms/rules/react.d.ts +1 -0
- package/dist/llms/rules/react.js +106 -0
- package/dist/llms/rules/request-response.d.ts +1 -0
- package/dist/llms/rules/request-response.js +209 -0
- package/dist/runtime/client.d.ts +17 -0
- package/dist/runtime/client.js +74 -0
- package/dist/runtime/clientNavigation.d.ts +4 -0
- package/dist/runtime/clientNavigation.js +53 -0
- package/dist/runtime/clientNavigation.test.d.ts +1 -0
- package/dist/runtime/clientNavigation.test.js +55 -0
- package/dist/runtime/constants.d.ts +1 -0
- package/dist/runtime/constants.js +1 -0
- package/dist/runtime/entries/auth.d.ts +1 -0
- package/dist/runtime/entries/auth.js +1 -0
- package/dist/runtime/entries/client.d.ts +4 -0
- package/dist/runtime/entries/client.js +4 -0
- package/dist/runtime/entries/clientSSR.d.ts +1 -0
- package/dist/runtime/entries/clientSSR.js +1 -0
- package/dist/runtime/entries/navigation.d.ts +1 -0
- package/dist/runtime/entries/navigation.js +1 -0
- package/dist/runtime/entries/no-react-server.d.ts +0 -0
- package/dist/runtime/entries/no-react-server.js +2 -0
- package/dist/runtime/entries/react-server-only.d.ts +0 -0
- package/dist/runtime/entries/react-server-only.js +2 -0
- package/dist/runtime/entries/router.d.ts +2 -0
- package/dist/runtime/entries/router.js +2 -0
- package/dist/runtime/entries/ssr.d.ts +1 -0
- package/dist/runtime/entries/ssr.js +1 -0
- package/dist/runtime/entries/worker.d.ts +9 -0
- package/dist/runtime/entries/worker.js +9 -0
- package/dist/runtime/error.d.ts +6 -0
- package/dist/runtime/error.js +8 -0
- package/dist/runtime/imports/ClientOnly.d.ts +3 -0
- package/dist/runtime/imports/ClientOnly.js +8 -0
- package/dist/runtime/imports/client.d.ts +4 -0
- package/dist/runtime/imports/client.js +33 -0
- package/dist/runtime/imports/ssr.d.ts +5 -0
- package/dist/runtime/imports/ssr.js +20 -0
- package/dist/runtime/imports/worker.d.ts +5 -0
- package/dist/runtime/imports/worker.js +22 -0
- package/dist/runtime/lib/auth/index.d.ts +1 -0
- package/dist/runtime/lib/auth/index.js +1 -0
- package/dist/runtime/lib/auth/session.d.ts +50 -0
- package/dist/runtime/lib/auth/session.js +148 -0
- package/dist/runtime/lib/db/DOWorkerDialect.d.ts +29 -0
- package/dist/runtime/lib/db/DOWorkerDialect.js +66 -0
- package/dist/runtime/lib/db/SqliteDurableObject.d.ts +14 -0
- package/dist/runtime/lib/db/SqliteDurableObject.js +42 -0
- package/dist/runtime/lib/db/createDb.d.ts +2 -0
- package/dist/runtime/lib/db/createDb.js +33 -0
- package/dist/runtime/lib/db/index.d.ts +4 -0
- package/dist/runtime/lib/db/index.js +3 -0
- package/dist/runtime/lib/db/migrations.d.ts +23 -0
- package/dist/runtime/lib/db/migrations.js +34 -0
- package/dist/runtime/lib/db/typeInference/assert.d.ts +2 -0
- package/dist/runtime/lib/db/typeInference/assert.js +1 -0
- package/dist/runtime/lib/db/typeInference/builders/alterColumn.d.ts +27 -0
- package/dist/runtime/lib/db/typeInference/builders/alterColumn.js +1 -0
- package/dist/runtime/lib/db/typeInference/builders/alterTable.d.ts +53 -0
- package/dist/runtime/lib/db/typeInference/builders/alterTable.js +1 -0
- package/dist/runtime/lib/db/typeInference/builders/columnDefinition.d.ts +26 -0
- package/dist/runtime/lib/db/typeInference/builders/columnDefinition.js +1 -0
- package/dist/runtime/lib/db/typeInference/builders/createTable.d.ts +49 -0
- package/dist/runtime/lib/db/typeInference/builders/createTable.js +1 -0
- package/dist/runtime/lib/db/typeInference/builders/createView.d.ts +17 -0
- package/dist/runtime/lib/db/typeInference/builders/createView.js +1 -0
- package/dist/runtime/lib/db/typeInference/builders/dropTable.d.ts +11 -0
- package/dist/runtime/lib/db/typeInference/builders/dropTable.js +1 -0
- package/dist/runtime/lib/db/typeInference/builders/dropView.d.ts +12 -0
- package/dist/runtime/lib/db/typeInference/builders/dropView.js +1 -0
- package/dist/runtime/lib/db/typeInference/builders/schema.d.ts +24 -0
- package/dist/runtime/lib/db/typeInference/builders/schema.js +1 -0
- package/dist/runtime/lib/db/typeInference/database.d.ts +27 -0
- package/dist/runtime/lib/db/typeInference/database.js +1 -0
- package/dist/runtime/lib/db/typeInference/typetests/alterTable.typetest.d.ts +1 -0
- package/dist/runtime/lib/db/typeInference/typetests/alterTable.typetest.js +360 -0
- package/dist/runtime/lib/db/typeInference/typetests/createTable.typetest.d.ts +1 -0
- package/dist/runtime/lib/db/typeInference/typetests/createTable.typetest.js +33 -0
- package/dist/runtime/lib/db/typeInference/typetests/dropTable.typetest.d.ts +1 -0
- package/dist/runtime/lib/db/typeInference/typetests/dropTable.typetest.js +143 -0
- package/dist/runtime/lib/db/typeInference/typetests/print.d.ts +3 -0
- package/dist/runtime/lib/db/typeInference/typetests/print.js +1 -0
- package/dist/runtime/lib/db/typeInference/typetests/testUtils.d.ts +2 -0
- package/dist/runtime/lib/db/typeInference/typetests/testUtils.js +1 -0
- package/dist/runtime/lib/db/typeInference/typetests/typeInference.typetest.d.ts +1 -0
- package/dist/runtime/lib/db/typeInference/typetests/typeInference.typetest.js +17 -0
- package/dist/runtime/lib/db/typeInference/utils.d.ts +82 -0
- package/dist/runtime/lib/db/typeInference/utils.js +2 -0
- package/dist/runtime/lib/debug.d.ts +2 -0
- package/dist/runtime/lib/debug.js +36 -0
- package/dist/runtime/lib/links.d.ts +14 -0
- package/dist/runtime/lib/links.js +38 -0
- package/dist/runtime/lib/realtime/client.d.ts +7 -0
- package/dist/runtime/lib/realtime/client.js +166 -0
- package/dist/runtime/lib/realtime/constants.d.ts +1 -0
- package/dist/runtime/lib/realtime/constants.js +1 -0
- package/dist/runtime/lib/realtime/durableObject.d.ts +29 -0
- package/dist/runtime/lib/realtime/durableObject.js +187 -0
- package/dist/runtime/lib/realtime/renderRealtimeClients.d.ts +7 -0
- package/dist/runtime/lib/realtime/renderRealtimeClients.js +6 -0
- package/dist/runtime/lib/realtime/shared.d.ts +10 -0
- package/dist/runtime/lib/realtime/shared.js +10 -0
- package/dist/runtime/lib/realtime/validateUpgradeRequest.d.ts +6 -0
- package/dist/runtime/lib/realtime/validateUpgradeRequest.js +29 -0
- package/dist/runtime/lib/realtime/worker.d.ts +3 -0
- package/dist/runtime/lib/realtime/worker.js +16 -0
- package/dist/runtime/lib/router.d.ts +56 -0
- package/dist/runtime/lib/router.js +210 -0
- package/dist/runtime/lib/router.test.d.ts +1 -0
- package/dist/runtime/lib/router.test.js +58 -0
- package/dist/runtime/lib/streams/consumeEventStream.d.ts +4 -0
- package/dist/runtime/lib/streams/consumeEventStream.js +13 -0
- package/dist/runtime/lib/turnstile/TurnstileScript.d.ts +1 -0
- package/dist/runtime/lib/turnstile/TurnstileScript.js +2 -0
- package/dist/runtime/lib/turnstile/turnstile.d.ts +3 -0
- package/dist/runtime/lib/turnstile/turnstile.js +3 -0
- package/dist/runtime/lib/turnstile/useTurnstile.d.ts +4 -0
- package/dist/runtime/lib/turnstile/useTurnstile.js +23 -0
- package/dist/runtime/lib/turnstile/verifyTurnstileToken.d.ts +4 -0
- package/dist/runtime/lib/turnstile/verifyTurnstileToken.js +15 -0
- package/dist/runtime/lib/utils.d.ts +1 -0
- package/dist/runtime/lib/utils.js +1 -0
- package/dist/runtime/register/client.d.ts +1 -0
- package/dist/runtime/register/client.js +5 -0
- package/dist/runtime/register/ssr.d.ts +3 -0
- package/dist/runtime/register/ssr.js +26 -0
- package/dist/runtime/register/worker.d.ts +4 -0
- package/dist/runtime/register/worker.js +42 -0
- package/dist/runtime/render/createClientManifest.d.ts +1 -0
- package/dist/runtime/render/createClientManifest.js +7 -0
- package/dist/runtime/render/createModuleMap.d.ts +1 -0
- package/dist/runtime/render/createModuleMap.js +13 -0
- package/dist/runtime/render/injectRSCPayload.d.ts +3 -0
- package/dist/runtime/render/injectRSCPayload.js +79 -0
- package/dist/runtime/render/renderRscThenableToHtmlStream.d.ts +9 -0
- package/dist/runtime/render/renderRscThenableToHtmlStream.js +49 -0
- package/dist/runtime/render/renderToRscStream.d.ts +5 -0
- package/dist/runtime/render/renderToRscStream.js +46 -0
- package/dist/runtime/render/renderToStream.d.ts +9 -0
- package/dist/runtime/render/renderToStream.js +27 -0
- package/dist/runtime/render/renderToString.d.ts +7 -0
- package/dist/runtime/render/renderToString.js +26 -0
- package/dist/runtime/render/transformRscToHtmlStream.d.ts +8 -0
- package/dist/runtime/render/transformRscToHtmlStream.js +19 -0
- package/dist/runtime/requestInfo/types.d.ts +11 -0
- package/dist/runtime/requestInfo/types.js +1 -0
- package/dist/runtime/requestInfo/worker.d.ts +5 -0
- package/dist/runtime/requestInfo/worker.js +33 -0
- package/dist/runtime/script.d.ts +5 -0
- package/dist/runtime/script.js +8 -0
- package/dist/runtime/ssrBridge.d.ts +2 -0
- package/dist/runtime/ssrBridge.js +11 -0
- package/dist/runtime/worker.d.ts +18 -0
- package/dist/runtime/worker.js +173 -0
- package/dist/scripts/__sdk.d.mts +1 -0
- package/dist/scripts/__sdk.mjs +14 -0
- package/dist/scripts/build-vendor-bundles.d.mts +1 -0
- package/dist/scripts/build-vendor-bundles.mjs +92 -0
- package/dist/scripts/debug-sync.d.mts +6 -0
- package/dist/scripts/debug-sync.mjs +224 -0
- package/dist/scripts/dev-init.d.mts +1 -0
- package/dist/scripts/dev-init.mjs +25 -0
- package/dist/scripts/ensure-deploy-env.d.mts +1 -0
- package/dist/scripts/ensure-deploy-env.mjs +271 -0
- package/dist/scripts/ensure-env.d.mts +1 -0
- package/dist/scripts/ensure-env.mjs +9 -0
- package/dist/scripts/migrate-new.d.mts +1 -0
- package/dist/scripts/migrate-new.mjs +51 -0
- package/dist/scripts/smoke-test.d.mts +1 -0
- package/dist/scripts/smoke-test.mjs +166 -0
- package/dist/scripts/worker-run.d.mts +1 -0
- package/dist/scripts/worker-run.mjs +82 -0
- package/dist/vite/aliasByEnvPlugin.d.mts +2 -0
- package/dist/vite/aliasByEnvPlugin.mjs +11 -0
- package/dist/vite/asyncSetupPlugin.d.mts +6 -0
- package/dist/vite/asyncSetupPlugin.mjs +23 -0
- package/dist/vite/checkIsUsingPrisma.d.mts +6 -0
- package/dist/vite/checkIsUsingPrisma.mjs +18 -0
- package/dist/vite/configPlugin.d.mts +9 -0
- package/dist/vite/configPlugin.mjs +169 -0
- package/dist/vite/copyPrismaWasmPlugin.d.mts +4 -0
- package/dist/vite/copyPrismaWasmPlugin.mjs +32 -0
- package/dist/vite/createDirectiveLookupPlugin.d.mts +21 -0
- package/dist/vite/createDirectiveLookupPlugin.mjs +231 -0
- package/dist/vite/customReactBuildPlugin.d.mts +4 -0
- package/dist/vite/customReactBuildPlugin.mjs +61 -0
- package/dist/vite/devServerTimingPlugin.d.mts +2 -0
- package/dist/vite/devServerTimingPlugin.mjs +24 -0
- package/dist/vite/directivesPlugin.d.mts +6 -0
- package/dist/vite/directivesPlugin.mjs +200 -0
- package/dist/vite/ensureAliasArray.d.mts +2 -0
- package/dist/vite/ensureAliasArray.mjs +17 -0
- package/dist/vite/findSpecifiers.d.mts +31 -0
- package/dist/vite/findSpecifiers.mjs +230 -0
- package/dist/vite/findSsrSpecifiers.d.mts +11 -0
- package/dist/vite/findSsrSpecifiers.mjs +67 -0
- package/dist/vite/hasDirective.d.mts +7 -0
- package/dist/vite/hasDirective.mjs +54 -0
- package/dist/vite/hasOwnCloudflareVitePlugin.d.mts +3 -0
- package/dist/vite/hasOwnCloudflareVitePlugin.mjs +14 -0
- package/dist/vite/index.d.mts +1 -0
- package/dist/vite/index.mjs +1 -0
- package/dist/vite/injectHmrPreambleJsxPlugin.d.mts +2 -0
- package/dist/vite/injectHmrPreambleJsxPlugin.mjs +22 -0
- package/dist/vite/injectVitePreamblePlugin.d.mts +4 -0
- package/dist/vite/injectVitePreamblePlugin.mjs +23 -0
- package/dist/vite/invalidateCacheIfPrismaClientChanged.d.mts +3 -0
- package/dist/vite/invalidateCacheIfPrismaClientChanged.mjs +27 -0
- package/dist/vite/invalidateModule.d.mts +6 -0
- package/dist/vite/invalidateModule.mjs +30 -0
- package/dist/vite/miniflareHMRPlugin.d.mts +10 -0
- package/dist/vite/miniflareHMRPlugin.mjs +209 -0
- package/dist/vite/miniflarePlugin.d.mts +9 -0
- package/dist/vite/miniflarePlugin.mjs +135 -0
- package/dist/vite/moveStaticAssetsPlugin.d.mts +4 -0
- package/dist/vite/moveStaticAssetsPlugin.mjs +12 -0
- package/dist/vite/normalizeModulePath.d.mts +1 -0
- package/dist/vite/normalizeModulePath.mjs +13 -0
- package/dist/vite/prismaPlugin.d.mts +4 -0
- package/dist/vite/prismaPlugin.mjs +43 -0
- package/dist/vite/reactConditionsResolverPlugin.d.mts +16 -0
- package/dist/vite/reactConditionsResolverPlugin.mjs +179 -0
- package/dist/vite/redwoodPlugin.d.mts +12 -0
- package/dist/vite/redwoodPlugin.mjs +105 -0
- package/dist/vite/requestUtils.d.mts +6 -0
- package/dist/vite/requestUtils.mjs +35 -0
- package/dist/vite/setupEnvFiles.d.mts +4 -0
- package/dist/vite/setupEnvFiles.mjs +31 -0
- package/dist/vite/ssrBridgePlugin.d.mts +7 -0
- package/dist/vite/ssrBridgePlugin.mjs +137 -0
- package/dist/vite/transformClientComponents.d.mts +12 -0
- package/dist/vite/transformClientComponents.mjs +116 -0
- package/dist/vite/transformClientComponents.test.d.mts +1 -0
- package/dist/vite/transformClientComponents.test.mjs +264 -0
- package/dist/vite/transformJsxScriptTagsPlugin.d.mts +8 -0
- package/dist/vite/transformJsxScriptTagsPlugin.mjs +315 -0
- package/dist/vite/transformJsxScriptTagsPlugin.test.d.mts +1 -0
- package/dist/vite/transformJsxScriptTagsPlugin.test.mjs +334 -0
- package/dist/vite/transformServerFunctions.d.mts +16 -0
- package/dist/vite/transformServerFunctions.mjs +296 -0
- package/dist/vite/transformServerFunctions.test.d.mts +1 -0
- package/dist/vite/transformServerFunctions.test.mjs +124 -0
- package/dist/vite/useClientLookupPlugin.d.mts +5 -0
- package/dist/vite/useClientLookupPlugin.mjs +15 -0
- package/dist/vite/useClientPlugin.d.mts +8 -0
- package/dist/vite/useClientPlugin.mjs +295 -0
- package/dist/vite/useClientPlugin.test.d.mts +1 -0
- package/dist/vite/useClientPlugin.test.mjs +1204 -0
- package/dist/vite/useServerLookupPlugin.d.mts +5 -0
- package/dist/vite/useServerLookupPlugin.mjs +15 -0
- package/dist/vite/useServerPlugin.d.mts +1 -0
- package/dist/vite/useServerPlugin.mjs +1 -0
- package/dist/vite/virtualPlugin.d.mts +2 -0
- package/dist/vite/virtualPlugin.mjs +18 -0
- package/dist/vite/vitePreamblePlugin.d.mts +1 -0
- package/dist/vite/vitePreamblePlugin.mjs +11 -0
- package/dist/worker/__ssr_bridge.js +8947 -0
- package/dist/worker/__ssr_bridge.js.map +1 -0
- package/package.json +1 -1
- package/dist/vite/invalidateClientModule.d.mts +0 -2
- package/dist/vite/invalidateClientModule.mjs +0 -8
- package/dist/vite/invalidateModule copy.d.mts +0 -2
- package/dist/vite/invalidateModule copy.mjs +0 -14
- package/dist/vite/invalidateSSRModule.d.mts +0 -2
- package/dist/vite/invalidateSSRModule.mjs +0 -7
- package/dist/vite/mode.d.mts +0 -5
- package/dist/vite/mode.mjs +0 -25
- package/dist/vite/modePlugin.d.mts +0 -2
- package/dist/vite/modePlugin.mjs +0 -10
- /package/dist/vite/{isJsFile.d.ts → isJsFile.d.mts} +0 -0
- /package/dist/vite/{isJsFile.js → isJsFile.mjs} +0 -0
|
@@ -0,0 +1,1041 @@
|
|
|
1
|
+
import * as os from "os";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { pathExists } from "fs-extra";
|
|
4
|
+
import { log } from "./constants.mjs";
|
|
5
|
+
import { mkdirp } from "fs-extra";
|
|
6
|
+
import { install, resolveBuildId, computeExecutablePath, detectBrowserPlatform, Browser as PuppeteerBrowser, } from "@puppeteer/browsers";
|
|
7
|
+
import puppeteer from "puppeteer-core";
|
|
8
|
+
import { takeScreenshot } from "./artifacts.mjs";
|
|
9
|
+
import { RETRIES } from "./constants.mjs";
|
|
10
|
+
import { $ } from "../$.mjs";
|
|
11
|
+
import { fail } from "./utils.mjs";
|
|
12
|
+
import { reportSmokeTestResult } from "./reporting.mjs";
|
|
13
|
+
import { updateTestStatus } from "./state.mjs";
|
|
14
|
+
/**
|
|
15
|
+
* Launch a browser instance
|
|
16
|
+
*/
|
|
17
|
+
export async function launchBrowser(browserPath, headless = true) {
|
|
18
|
+
// Get browser path if not provided
|
|
19
|
+
if (!browserPath) {
|
|
20
|
+
log("Getting browser executable path");
|
|
21
|
+
browserPath = await getBrowserPath();
|
|
22
|
+
}
|
|
23
|
+
console.log(`🚀 Launching browser from ${browserPath} (headless: ${headless})`);
|
|
24
|
+
log("Starting browser with puppeteer");
|
|
25
|
+
return await puppeteer.launch({
|
|
26
|
+
executablePath: browserPath,
|
|
27
|
+
headless,
|
|
28
|
+
args: ["--no-sandbox", "--disable-setuid-sandbox"],
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Get the browser executable path
|
|
33
|
+
*/
|
|
34
|
+
export async function getBrowserPath(testOptions) {
|
|
35
|
+
console.log("Finding Chrome executable...");
|
|
36
|
+
// First try using environment variable if set
|
|
37
|
+
if (process.env.CHROME_PATH) {
|
|
38
|
+
console.log(`Using Chrome from environment variable: ${process.env.CHROME_PATH}`);
|
|
39
|
+
return process.env.CHROME_PATH;
|
|
40
|
+
}
|
|
41
|
+
// Detect platform
|
|
42
|
+
log("Detecting platform");
|
|
43
|
+
const platform = detectBrowserPlatform();
|
|
44
|
+
if (!platform) {
|
|
45
|
+
log("ERROR: Failed to detect browser platform");
|
|
46
|
+
throw new Error("Failed to detect browser platform");
|
|
47
|
+
}
|
|
48
|
+
log("Detected platform: %s", platform);
|
|
49
|
+
// Define a consistent cache directory path in system temp folder
|
|
50
|
+
const rwCacheDir = join(os.tmpdir(), "redwoodjs-smoke-test-cache");
|
|
51
|
+
await mkdirp(rwCacheDir);
|
|
52
|
+
log("Using cache directory: %s", rwCacheDir);
|
|
53
|
+
// Determine browser type based on headless option
|
|
54
|
+
const browser = testOptions?.headless === false
|
|
55
|
+
? PuppeteerBrowser.CHROME
|
|
56
|
+
: PuppeteerBrowser.CHROMEHEADLESSSHELL;
|
|
57
|
+
log(`Using browser type: ${browser}`);
|
|
58
|
+
// Resolve the buildId for the stable Chrome version - do this once
|
|
59
|
+
log("Resolving Chrome buildId for stable channel");
|
|
60
|
+
const buildId = await resolveBuildId(browser, platform, "stable");
|
|
61
|
+
log("Resolved buildId: %s", buildId);
|
|
62
|
+
// Create installation options - use them consistently
|
|
63
|
+
const installOptions = {
|
|
64
|
+
browser,
|
|
65
|
+
platform,
|
|
66
|
+
cacheDir: rwCacheDir,
|
|
67
|
+
buildId,
|
|
68
|
+
unpack: true,
|
|
69
|
+
};
|
|
70
|
+
try {
|
|
71
|
+
// Try to compute the path first (this will check if it's installed)
|
|
72
|
+
log("Attempting to find existing Chrome installation");
|
|
73
|
+
const path = computeExecutablePath(installOptions);
|
|
74
|
+
if (await pathExists(path)) {
|
|
75
|
+
console.log(`Found existing Chrome at: ${path}`);
|
|
76
|
+
return path;
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
throw new Error("Chrome not found at: " + path);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
// If path computation fails, install Chrome
|
|
84
|
+
console.log("No Chrome installation found. Installing Chrome...");
|
|
85
|
+
// Add better error handling for the install step
|
|
86
|
+
try {
|
|
87
|
+
console.log("Downloading Chrome (this may take a few minutes)...");
|
|
88
|
+
await install(installOptions);
|
|
89
|
+
console.log("✅ Chrome installation completed successfully");
|
|
90
|
+
// Now compute the path for the installed browser
|
|
91
|
+
const path = computeExecutablePath(installOptions);
|
|
92
|
+
console.log(`Installed and using Chrome at: ${path}`);
|
|
93
|
+
return path;
|
|
94
|
+
}
|
|
95
|
+
catch (installError) {
|
|
96
|
+
// Provide more detailed error about the browser download failure
|
|
97
|
+
log("ERROR: Failed to download/install Chrome: %O", installError);
|
|
98
|
+
console.error(`❌ Failed to download/install Chrome browser.`);
|
|
99
|
+
console.error(`This is likely a network issue or the browser download URL is unavailable.`);
|
|
100
|
+
console.error(`Error details: ${installError instanceof Error ? installError.message : String(installError)}`);
|
|
101
|
+
// For debug builds, show the full error stack if available
|
|
102
|
+
if (installError instanceof Error && installError.stack) {
|
|
103
|
+
log("Error stack: %s", installError.stack);
|
|
104
|
+
}
|
|
105
|
+
console.log("\nPossible solutions:");
|
|
106
|
+
console.log("1. Check your internet connection");
|
|
107
|
+
console.log("2. Set CHROME_PATH environment variable to an existing Chrome installation");
|
|
108
|
+
console.log("3. Install Chrome manually and run the tests again");
|
|
109
|
+
throw new Error(`Failed to install Chrome browser: ${installError instanceof Error ? installError.message : String(installError)}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Check a URL by performing smoke tests and realtime upgrade
|
|
115
|
+
*/
|
|
116
|
+
export async function checkUrl(url, artifactDir, browserPath, headless = true, bail = false, skipClient = false, environment = "Development", realtime = false, targetDir, skipHmr = false) {
|
|
117
|
+
console.log(`🔍 Testing URL: ${url}`);
|
|
118
|
+
log("Launching browser");
|
|
119
|
+
let browser;
|
|
120
|
+
try {
|
|
121
|
+
browser = await launchBrowser(browserPath, headless);
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
// Use fail() directly for browser launch errors
|
|
125
|
+
await fail(error, 1, `${environment} - Browser Launch`);
|
|
126
|
+
return; // This will never be reached due to fail() exiting
|
|
127
|
+
}
|
|
128
|
+
// Track failures to report at the end
|
|
129
|
+
let hasFailures = false;
|
|
130
|
+
let initialTestError = null;
|
|
131
|
+
let realtimeTestError = null;
|
|
132
|
+
// Store timestamp values between test phases
|
|
133
|
+
const timestampState = {
|
|
134
|
+
initialServerValue: 23, // This is the default initial module-level value
|
|
135
|
+
clientUpdatedValue: null,
|
|
136
|
+
};
|
|
137
|
+
try {
|
|
138
|
+
log("Opening new page");
|
|
139
|
+
const page = await browser.newPage();
|
|
140
|
+
page.setDefaultNavigationTimeout(30000);
|
|
141
|
+
log("Set navigation timeout: %dms", 30000);
|
|
142
|
+
if (realtime) {
|
|
143
|
+
// If realtime flag is set, use the simplified flow that only does realtime testing
|
|
144
|
+
log("Using realtime-only flow (--realtime option enabled)");
|
|
145
|
+
console.log("⏩ Skipping initial smoke tests and upgrade step (--realtime option enabled)");
|
|
146
|
+
// Skip upgradeToRealtime and just run the realtime tests directly
|
|
147
|
+
try {
|
|
148
|
+
log("Performing realtime-only smoke test");
|
|
149
|
+
await checkUrlSmoke(page, url, true, bail, skipClient, environment, timestampState, targetDir, skipHmr);
|
|
150
|
+
// Take a screenshot of the realtime test
|
|
151
|
+
await takeScreenshot(page, url, artifactDir, `${environment.toLowerCase()}-realtime-passed`);
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
hasFailures = true;
|
|
155
|
+
realtimeTestError =
|
|
156
|
+
error instanceof Error ? error : new Error(String(error));
|
|
157
|
+
log("Error during realtime-only test: %O", error);
|
|
158
|
+
console.error(`❌ Realtime test failed: ${realtimeTestError.message}`);
|
|
159
|
+
// Take a failure screenshot
|
|
160
|
+
await takeScreenshot(page, url, artifactDir, `${environment.toLowerCase()}-realtime-failed`).catch((e) => log("Failed to take error screenshot: %O", e));
|
|
161
|
+
// If bail is true, propagate the error
|
|
162
|
+
if (bail) {
|
|
163
|
+
throw error;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
// Normal flow with both initial and realtime tests
|
|
169
|
+
// Initial smoke test
|
|
170
|
+
log("Performing initial smoke test");
|
|
171
|
+
let initialTestStatus = "passed";
|
|
172
|
+
try {
|
|
173
|
+
await checkUrlSmoke(page, url, false, bail, skipClient, environment, timestampState, targetDir, skipHmr);
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
hasFailures = true;
|
|
177
|
+
initialTestStatus = "failed";
|
|
178
|
+
initialTestError =
|
|
179
|
+
error instanceof Error ? error : new Error(String(error));
|
|
180
|
+
log("Error during initial smoke test: %O", error);
|
|
181
|
+
console.error(`❌ Initial smoke test failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
182
|
+
// If bail is true, stop the tests
|
|
183
|
+
if (bail) {
|
|
184
|
+
throw error;
|
|
185
|
+
}
|
|
186
|
+
console.log("Continuing with realtime upgrade test since --bail is not enabled...");
|
|
187
|
+
}
|
|
188
|
+
// Take a screenshot after initial test
|
|
189
|
+
await takeScreenshot(page, url, artifactDir, `${environment.toLowerCase()}-initial-${initialTestStatus}`);
|
|
190
|
+
// Upgrade to realtime and check again
|
|
191
|
+
log("Upgrading to realtime");
|
|
192
|
+
let realtimeTestStatus = "passed";
|
|
193
|
+
try {
|
|
194
|
+
await upgradeToRealtime(page, environment, bail);
|
|
195
|
+
log("Performing post-upgrade smoke test");
|
|
196
|
+
await checkUrlSmoke(page, url, true, bail, skipClient, environment, timestampState, targetDir, skipHmr);
|
|
197
|
+
}
|
|
198
|
+
catch (error) {
|
|
199
|
+
hasFailures = true;
|
|
200
|
+
realtimeTestStatus = "failed";
|
|
201
|
+
realtimeTestError =
|
|
202
|
+
error instanceof Error ? error : new Error(String(error));
|
|
203
|
+
log("Error during realtime smoke test: %O", error);
|
|
204
|
+
console.error(`❌ Realtime smoke test failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
205
|
+
// If bail is true, stop the tests
|
|
206
|
+
if (bail) {
|
|
207
|
+
throw error;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
// Take a screenshot after realtime test
|
|
211
|
+
await takeScreenshot(page, url, artifactDir, `${environment.toLowerCase()}-realtime-${realtimeTestStatus}`);
|
|
212
|
+
}
|
|
213
|
+
// If there were failures, propagate them after taking screenshots
|
|
214
|
+
if (hasFailures) {
|
|
215
|
+
// Combine errors or just throw the one that happened
|
|
216
|
+
if (initialTestError && realtimeTestError) {
|
|
217
|
+
throw new Error(`Multiple test failures: Initial test: ${initialTestError.message}, Realtime test: ${realtimeTestError.message}`);
|
|
218
|
+
}
|
|
219
|
+
else if (initialTestError) {
|
|
220
|
+
throw initialTestError;
|
|
221
|
+
}
|
|
222
|
+
else if (realtimeTestError) {
|
|
223
|
+
throw realtimeTestError;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
catch (error) {
|
|
228
|
+
log("Error during URL check: %O", error);
|
|
229
|
+
await browser
|
|
230
|
+
.close()
|
|
231
|
+
.catch((e) => log("Error closing browser: %O", e));
|
|
232
|
+
throw error;
|
|
233
|
+
}
|
|
234
|
+
finally {
|
|
235
|
+
log("Closing browser");
|
|
236
|
+
await browser
|
|
237
|
+
.close()
|
|
238
|
+
.catch((e) => log("Error closing browser: %O", e));
|
|
239
|
+
}
|
|
240
|
+
log("URL check completed successfully");
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Check smoke test status for a specific URL
|
|
244
|
+
*/
|
|
245
|
+
export async function checkUrlSmoke(page, url, isRealtime, bail = false, skipClient = false, environment = "Development", timestampState, targetDir, skipHmr = false) {
|
|
246
|
+
const phase = isRealtime ? "Post-upgrade" : "Initial";
|
|
247
|
+
console.log(`🔍 Testing ${phase} smoke tests at ${url}`);
|
|
248
|
+
// Parse the base URL and path to properly handle smoke test queries
|
|
249
|
+
const parsedUrl = new URL(url);
|
|
250
|
+
log("Parsed URL: %O", {
|
|
251
|
+
origin: parsedUrl.origin,
|
|
252
|
+
pathname: parsedUrl.pathname,
|
|
253
|
+
search: parsedUrl.search,
|
|
254
|
+
});
|
|
255
|
+
// Add __smoke_test query parameter, preserving any existing query parameters
|
|
256
|
+
if (parsedUrl.searchParams.has("__smoke_test")) {
|
|
257
|
+
console.log(`URL already has __smoke_test parameter: ${url}`);
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
parsedUrl.searchParams.append("__smoke_test", "1");
|
|
261
|
+
log("Added __smoke_test parameter to URL");
|
|
262
|
+
}
|
|
263
|
+
// Navigate to smoke test page
|
|
264
|
+
const smokeUrl = parsedUrl.toString();
|
|
265
|
+
console.log(`🔍 Accessing smoke test page: ${smokeUrl}`);
|
|
266
|
+
await page.goto(smokeUrl, { waitUntil: "networkidle0" });
|
|
267
|
+
log("Page loaded successfully");
|
|
268
|
+
// Track failures to report at the end
|
|
269
|
+
let hasFailures = false;
|
|
270
|
+
let initialServerTestError = null;
|
|
271
|
+
let clientTestError = null;
|
|
272
|
+
let serverRenderCheckError = null;
|
|
273
|
+
let hmrTestError = null;
|
|
274
|
+
// Step 1: Run initial server-side smoke test to check the server state
|
|
275
|
+
log("Running initial server-side smoke test");
|
|
276
|
+
let initialServerResult;
|
|
277
|
+
try {
|
|
278
|
+
// For initial checks: use the fixed initial value (23)
|
|
279
|
+
// For realtime checks: if we've previously updated the value, use that instead of 23
|
|
280
|
+
// For HMR tests: if HMR happened, we'll be back to the initial value (23)
|
|
281
|
+
const expectedValue = isRealtime && timestampState.clientUpdatedValue !== null && skipHmr
|
|
282
|
+
? timestampState.clientUpdatedValue
|
|
283
|
+
: timestampState.initialServerValue;
|
|
284
|
+
// Check that the server is returning the expected value
|
|
285
|
+
initialServerResult = await checkServerSmoke(page, phase, environment, bail, expectedValue, false);
|
|
286
|
+
log(`${phase} server-side check passed - module variable has expected value ${expectedValue}`);
|
|
287
|
+
// Store the current timestamp for potential future reference
|
|
288
|
+
if (initialServerResult && initialServerResult.timestamp) {
|
|
289
|
+
timestampState.initialServerValue = initialServerResult.timestamp;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
hasFailures = true;
|
|
294
|
+
initialServerTestError =
|
|
295
|
+
error instanceof Error ? error : new Error(String(error));
|
|
296
|
+
log("Error during initial server-side smoke test: %O", error);
|
|
297
|
+
console.error(`❌ ${phase} server-side smoke test failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
298
|
+
// If bail is true, stop the tests
|
|
299
|
+
if (bail) {
|
|
300
|
+
throw error;
|
|
301
|
+
}
|
|
302
|
+
console.log("Continuing with client-side smoke test since --bail is not enabled...");
|
|
303
|
+
}
|
|
304
|
+
// Skip client tests if requested
|
|
305
|
+
if (skipClient) {
|
|
306
|
+
log("Skipping client-side smoke test as requested");
|
|
307
|
+
console.log("⏩ Skipping client-side smoke test as requested");
|
|
308
|
+
// If we're running HMR tests and have a target directory
|
|
309
|
+
if (!skipHmr && targetDir) {
|
|
310
|
+
try {
|
|
311
|
+
// Run server HMR test if client tests are skipped
|
|
312
|
+
log("Running server HMR test");
|
|
313
|
+
await testServerComponentHmr(page, targetDir, phase, environment, bail);
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
hasFailures = true;
|
|
317
|
+
hmrTestError =
|
|
318
|
+
error instanceof Error ? error : new Error(String(error));
|
|
319
|
+
log("Error during HMR test: %O", error);
|
|
320
|
+
if (bail) {
|
|
321
|
+
throw error;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
// Step 2: Run client-side smoke test to update the server timestamp
|
|
328
|
+
log("Running client-side smoke test");
|
|
329
|
+
let clientResult;
|
|
330
|
+
try {
|
|
331
|
+
clientResult = await checkClientSmoke(page, phase, environment, bail);
|
|
332
|
+
// Store the client-updated timestamp for further tests
|
|
333
|
+
if (clientResult && clientResult.clientTimestamp) {
|
|
334
|
+
timestampState.clientUpdatedValue = clientResult.clientTimestamp;
|
|
335
|
+
log(`Saved client timestamp ${clientResult.clientTimestamp} for verification`);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
catch (error) {
|
|
339
|
+
hasFailures = true;
|
|
340
|
+
clientTestError = error instanceof Error ? error : new Error(String(error));
|
|
341
|
+
log("Error during client-side smoke test: %O", error);
|
|
342
|
+
console.error(`❌ Client-side smoke test failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
343
|
+
// If bail is true, stop the tests
|
|
344
|
+
if (bail) {
|
|
345
|
+
throw error;
|
|
346
|
+
}
|
|
347
|
+
console.log("Continuing with server render check since --bail is not enabled...");
|
|
348
|
+
}
|
|
349
|
+
// Step 3: Check if the server has rendered with the updated timestamp (server render check)
|
|
350
|
+
if (clientResult && clientResult.clientTimestamp) {
|
|
351
|
+
log("Running server render check with client timestamp");
|
|
352
|
+
// Wait a moment for any server renders to complete
|
|
353
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
354
|
+
try {
|
|
355
|
+
await checkServerSmoke(page, phase, environment, bail, clientResult.clientTimestamp, // Expected to match the client-set timestamp
|
|
356
|
+
true);
|
|
357
|
+
log("Server render check passed - server has updated with client timestamp");
|
|
358
|
+
}
|
|
359
|
+
catch (error) {
|
|
360
|
+
hasFailures = true;
|
|
361
|
+
serverRenderCheckError =
|
|
362
|
+
error instanceof Error ? error : new Error(String(error));
|
|
363
|
+
log("Error during server render check: %O", error);
|
|
364
|
+
console.error(`❌ Server render check failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
365
|
+
// If bail is true, stop the tests
|
|
366
|
+
if (bail) {
|
|
367
|
+
throw error;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
log("Skipping server render check - no client timestamp available");
|
|
373
|
+
// Update test status for server render check as skipped
|
|
374
|
+
const env = environment === "Development" ? "dev" : "production";
|
|
375
|
+
const serverRenderKey = phase === "Initial" || !phase
|
|
376
|
+
? "initialServerRenderCheck"
|
|
377
|
+
: "realtimeServerRenderCheck";
|
|
378
|
+
updateTestStatus(env, serverRenderKey, "SKIPPED");
|
|
379
|
+
}
|
|
380
|
+
// Step 4: Run HMR tests if target directory is provided and HMR tests are not skipped
|
|
381
|
+
if (!skipHmr && targetDir && environment === "Development") {
|
|
382
|
+
log(`Starting HMR tests for ${phase} phase`);
|
|
383
|
+
console.log(`\n🔄 Running HMR tests for ${phase} phase...`);
|
|
384
|
+
try {
|
|
385
|
+
// Test server component HMR
|
|
386
|
+
await testServerComponentHmr(page, targetDir, phase, environment, bail);
|
|
387
|
+
// Test client component HMR if client tests aren't skipped
|
|
388
|
+
if (!skipClient) {
|
|
389
|
+
await testClientComponentHmr(page, targetDir, phase, environment, bail);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
catch (error) {
|
|
393
|
+
hasFailures = true;
|
|
394
|
+
hmrTestError = error instanceof Error ? error : new Error(String(error));
|
|
395
|
+
log("Error during HMR tests: %O", error);
|
|
396
|
+
console.error(`❌ HMR tests failed: ${hmrTestError.message}`);
|
|
397
|
+
// If bail is true, stop the tests
|
|
398
|
+
if (bail) {
|
|
399
|
+
throw error;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
else {
|
|
404
|
+
log("Skipping HMR tests - targetDir not provided or skipHmr is true or not in Development environment");
|
|
405
|
+
if (skipHmr) {
|
|
406
|
+
console.log("⏩ Skipping HMR tests as requested");
|
|
407
|
+
}
|
|
408
|
+
else if (!targetDir) {
|
|
409
|
+
console.log("⏩ Skipping HMR tests - target directory not provided");
|
|
410
|
+
}
|
|
411
|
+
else if (environment !== "Development") {
|
|
412
|
+
console.log(`⏩ Skipping HMR tests - not applicable in ${environment} environment`);
|
|
413
|
+
}
|
|
414
|
+
// Update test status for HMR tests as skipped
|
|
415
|
+
const env = environment === "Development" ? "dev" : "production";
|
|
416
|
+
updateTestStatus(env, phase === "Initial" ? "initialServerHmr" : "realtimeServerHmr", "SKIPPED");
|
|
417
|
+
updateTestStatus(env, phase === "Initial" ? "initialClientHmr" : "realtimeClientHmr", "SKIPPED");
|
|
418
|
+
}
|
|
419
|
+
// If there were failures, propagate them
|
|
420
|
+
if (hasFailures) {
|
|
421
|
+
// Combine errors
|
|
422
|
+
const errors = [];
|
|
423
|
+
if (initialServerTestError) {
|
|
424
|
+
errors.push(`Initial server test: ${initialServerTestError.message}`);
|
|
425
|
+
}
|
|
426
|
+
if (clientTestError) {
|
|
427
|
+
errors.push(`Client test: ${clientTestError.message}`);
|
|
428
|
+
}
|
|
429
|
+
if (serverRenderCheckError) {
|
|
430
|
+
errors.push(`Server render check: ${serverRenderCheckError.message}`);
|
|
431
|
+
}
|
|
432
|
+
if (hmrTestError) {
|
|
433
|
+
errors.push(`HMR test: ${hmrTestError.message}`);
|
|
434
|
+
}
|
|
435
|
+
throw new Error(`Multiple test failures: ${errors.join(", ")}`);
|
|
436
|
+
}
|
|
437
|
+
log("URL smoke test completed successfully");
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Check server-side smoke test status
|
|
441
|
+
*/
|
|
442
|
+
export async function checkServerSmoke(page, phase = "", environment = "Development", bail = false, expectedTimestamp, isServerRenderCheck = false) {
|
|
443
|
+
const checkType = isServerRenderCheck
|
|
444
|
+
? "Server Render Check"
|
|
445
|
+
: "Initial Check";
|
|
446
|
+
console.log(`🔍 Testing server-side smoke test ${phase ? `(${phase})` : ""} - ${checkType}`);
|
|
447
|
+
// Determine the environment and test key for state update
|
|
448
|
+
const env = environment === "Development" ? "dev" : "production";
|
|
449
|
+
// Determine the appropriate test key based on phase and whether this is a server render check
|
|
450
|
+
let testKey;
|
|
451
|
+
if (isServerRenderCheck) {
|
|
452
|
+
// This is a server render check - use the appropriate key based on phase
|
|
453
|
+
testKey =
|
|
454
|
+
phase === "Initial" || !phase
|
|
455
|
+
? "initialServerRenderCheck"
|
|
456
|
+
: "realtimeServerRenderCheck";
|
|
457
|
+
}
|
|
458
|
+
else {
|
|
459
|
+
// Regular server-side check
|
|
460
|
+
testKey =
|
|
461
|
+
phase === "Initial" || !phase
|
|
462
|
+
? "initialServerSide"
|
|
463
|
+
: "realtimeServerSide";
|
|
464
|
+
}
|
|
465
|
+
const result = await page.evaluate(async (expectedTimestamp) => {
|
|
466
|
+
try {
|
|
467
|
+
// Look for smoke test status indicator in the page
|
|
468
|
+
const smokeElement = document.querySelector('[data-testid="health-status"]');
|
|
469
|
+
if (!smokeElement) {
|
|
470
|
+
return {
|
|
471
|
+
status: "error",
|
|
472
|
+
verificationPassed: false,
|
|
473
|
+
error: "Smoke test status element not found in the page",
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
// Check if required attributes exist
|
|
477
|
+
const status = smokeElement.getAttribute("data-status");
|
|
478
|
+
if (status === null) {
|
|
479
|
+
return {
|
|
480
|
+
status: "error",
|
|
481
|
+
verificationPassed: false,
|
|
482
|
+
error: "data-status attribute is missing on health-status element",
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
const timestamp = parseInt(smokeElement.getAttribute("data-timestamp") || "0", 10);
|
|
486
|
+
const serverTimestamp = parseInt(smokeElement.getAttribute("data-server-timestamp") || "0", 10);
|
|
487
|
+
// If an expected timestamp is provided, verify it matches
|
|
488
|
+
let verificationPassed = true;
|
|
489
|
+
let verificationError;
|
|
490
|
+
if (expectedTimestamp) {
|
|
491
|
+
verificationPassed = timestamp === expectedTimestamp;
|
|
492
|
+
if (!verificationPassed) {
|
|
493
|
+
verificationError = `Server timestamp (${timestamp}) does not match expected (${expectedTimestamp})`;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
return {
|
|
497
|
+
status: status || "error",
|
|
498
|
+
verificationPassed: status === "ok" && verificationPassed,
|
|
499
|
+
timestamp,
|
|
500
|
+
serverTimestamp,
|
|
501
|
+
error: verificationError ||
|
|
502
|
+
(status !== "ok" ? "Smoke test did not return ok status" : undefined),
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
catch (error) {
|
|
506
|
+
return {
|
|
507
|
+
status: "error",
|
|
508
|
+
verificationPassed: false,
|
|
509
|
+
error: error instanceof Error ? error.message : String(error),
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
}, expectedTimestamp);
|
|
513
|
+
log("Server-side smoke test result: %O", result);
|
|
514
|
+
// Update test status based on result
|
|
515
|
+
updateTestStatus(env, testKey, result.verificationPassed ? "PASSED" : "FAILED");
|
|
516
|
+
// Report the result (this no longer throws errors)
|
|
517
|
+
reportSmokeTestResult(result, isServerRenderCheck ? "Server Render Check" : "Server-side", phase, environment);
|
|
518
|
+
// Handle the error if verification failed
|
|
519
|
+
if (!result.verificationPassed) {
|
|
520
|
+
const errorMessage = `${environment} - ${phase ? `(${phase}) ` : ""}${isServerRenderCheck ? "Server Render Check" : "Server-side smoke test"} failed. Status: ${result.status}${result.error ? `. Error: ${result.error}` : ""}`;
|
|
521
|
+
if (bail) {
|
|
522
|
+
// If bail is true, call fail() which will exit the process
|
|
523
|
+
await fail(new Error(errorMessage), 1, `${environment} - ${isServerRenderCheck ? "Server Render Check" : "Server-side Smoke Test"} (${phase})`);
|
|
524
|
+
}
|
|
525
|
+
else {
|
|
526
|
+
// Otherwise throw an error that can be caught by the caller
|
|
527
|
+
throw new Error(errorMessage);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
return result;
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* Check client-side smoke test if refresh button is available
|
|
534
|
+
*/
|
|
535
|
+
export async function checkClientSmoke(page, phase = "", environment = "Development", bail = false) {
|
|
536
|
+
console.log(`🔍 Testing client-side smoke test ${phase ? `(${phase})` : ""}`);
|
|
537
|
+
// Determine the environment and test key for state update
|
|
538
|
+
const env = environment === "Development" ? "dev" : "production";
|
|
539
|
+
const testKey = phase === "Initial" || !phase ? "initialClientSide" : "realtimeClientSide";
|
|
540
|
+
// Check if refresh button exists
|
|
541
|
+
log("Checking for refresh button");
|
|
542
|
+
const refreshButtonExists = await page.evaluate(() => {
|
|
543
|
+
const button = document.querySelector('[data-testid="refresh-health"]');
|
|
544
|
+
return !!button;
|
|
545
|
+
});
|
|
546
|
+
if (!refreshButtonExists) {
|
|
547
|
+
log("No client-side refresh button found");
|
|
548
|
+
console.warn("⚠️ No client-side refresh button found - this is expected only if testing a non-smoke test page");
|
|
549
|
+
// Look for any other evidence that the page is working
|
|
550
|
+
log("Checking if page content is valid HTML");
|
|
551
|
+
const pageContent = await page.content();
|
|
552
|
+
if (!pageContent.includes("<!DOCTYPE html>")) {
|
|
553
|
+
log("ERROR: Page doesn't appear to be a valid HTML document");
|
|
554
|
+
throw new Error("Page doesn't appear to be a valid HTML document");
|
|
555
|
+
}
|
|
556
|
+
// Check if we're on a smoke test page - in which case missing the refresh button is a failure
|
|
557
|
+
const currentUrl = page.url();
|
|
558
|
+
log("Current URL: %s", currentUrl);
|
|
559
|
+
if (currentUrl.includes("__smoke_test")) {
|
|
560
|
+
log("ERROR: Smoke test page is missing the refresh-health button");
|
|
561
|
+
throw new Error("Smoke test page is missing the refresh-health button - this is a test failure");
|
|
562
|
+
}
|
|
563
|
+
console.log("ℹ️ Basic page structure verified, continuing without client-side smoke test");
|
|
564
|
+
return null;
|
|
565
|
+
}
|
|
566
|
+
log("Clicking refresh button to trigger client-side smoke test");
|
|
567
|
+
await page.click('[data-testid="refresh-health"]');
|
|
568
|
+
// Wait for client-side update to complete
|
|
569
|
+
log("Waiting for client-side test to complete");
|
|
570
|
+
try {
|
|
571
|
+
await page.waitForFunction(() => {
|
|
572
|
+
const clientIndicator = document.querySelector("#smoke-test-client-timestamp");
|
|
573
|
+
return (clientIndicator &&
|
|
574
|
+
+(clientIndicator.getAttribute("data-client-timestamp") ?? "0") > 0);
|
|
575
|
+
}, { timeout: 5000 });
|
|
576
|
+
log("Client-side test completed");
|
|
577
|
+
}
|
|
578
|
+
catch (error) {
|
|
579
|
+
log("ERROR: Timed out waiting for client-side smoke test to complete: %O", error);
|
|
580
|
+
throw new Error(`Timed out waiting for client-side smoke test to complete: ${error instanceof Error ? error.message : String(error)}`);
|
|
581
|
+
}
|
|
582
|
+
const result = await page.evaluate(async () => {
|
|
583
|
+
try {
|
|
584
|
+
const smokeElement = document.querySelector('[data-testid="health-status"]');
|
|
585
|
+
if (!smokeElement) {
|
|
586
|
+
return {
|
|
587
|
+
status: "error",
|
|
588
|
+
verificationPassed: false,
|
|
589
|
+
error: "Smoke test status element not found in the page",
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
// Check if required client attributes exist
|
|
593
|
+
const clientTimestampElement = document.querySelector("#smoke-test-client-timestamp");
|
|
594
|
+
if (!clientTimestampElement) {
|
|
595
|
+
return {
|
|
596
|
+
status: "error",
|
|
597
|
+
verificationPassed: false,
|
|
598
|
+
error: "Client timestamp element not found in the page",
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
// Check if required client attributes exist
|
|
602
|
+
const clientTimestampAttr = clientTimestampElement.getAttribute("data-client-timestamp");
|
|
603
|
+
if (clientTimestampAttr === null) {
|
|
604
|
+
return {
|
|
605
|
+
status: "error",
|
|
606
|
+
verificationPassed: false,
|
|
607
|
+
error: "data-client-timestamp attribute is missing",
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
const clientTimestamp = parseInt(clientTimestampAttr, 10);
|
|
611
|
+
// Check if status attribute exists
|
|
612
|
+
const clientStatus = clientTimestampElement.getAttribute("data-status");
|
|
613
|
+
if (clientStatus === null) {
|
|
614
|
+
return {
|
|
615
|
+
status: "error",
|
|
616
|
+
verificationPassed: false,
|
|
617
|
+
error: "data-status attribute is missing on client timestamp element",
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
// Check if verified attribute exists
|
|
621
|
+
if (clientTimestampElement.getAttribute("data-verified") === null) {
|
|
622
|
+
return {
|
|
623
|
+
status: "error",
|
|
624
|
+
verificationPassed: false,
|
|
625
|
+
error: "data-verified attribute is missing on client timestamp element",
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
// Get client verification result directly
|
|
629
|
+
const verificationPassed = clientTimestampElement.getAttribute("data-verified") === "true";
|
|
630
|
+
return {
|
|
631
|
+
status: clientStatus,
|
|
632
|
+
verificationPassed: clientStatus === "ok" && verificationPassed,
|
|
633
|
+
clientTimestamp,
|
|
634
|
+
error: clientStatus !== "ok"
|
|
635
|
+
? "Client smoke test did not return ok status"
|
|
636
|
+
: undefined,
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
catch (error) {
|
|
640
|
+
return {
|
|
641
|
+
status: "error",
|
|
642
|
+
verificationPassed: false,
|
|
643
|
+
error: error instanceof Error ? error.message : String(error),
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
});
|
|
647
|
+
log("Client-side smoke test result: %O", result);
|
|
648
|
+
// Update test status based on result
|
|
649
|
+
updateTestStatus(env, testKey, result.verificationPassed ? "PASSED" : "FAILED");
|
|
650
|
+
// Report the result (this no longer throws errors)
|
|
651
|
+
reportSmokeTestResult(result, "Client-side", phase, environment);
|
|
652
|
+
// Handle the error if verification failed
|
|
653
|
+
if (!result.verificationPassed) {
|
|
654
|
+
const errorMessage = `${environment} - ${phase ? `(${phase}) ` : ""}Client-side smoke test failed. Status: ${result.status}${result.error ? `. Error: ${result.error}` : ""}`;
|
|
655
|
+
if (bail) {
|
|
656
|
+
// If bail is true, call fail() which will exit the process
|
|
657
|
+
await fail(new Error(errorMessage), 1, `${environment} - Client-side Smoke Test (${phase})`);
|
|
658
|
+
}
|
|
659
|
+
else {
|
|
660
|
+
// Otherwise throw an error that can be caught by the caller
|
|
661
|
+
throw new Error(errorMessage);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
return result;
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Upgrade to realtime mode
|
|
668
|
+
*/
|
|
669
|
+
export async function upgradeToRealtime(page, environment = "Development", // Add environment parameter with default
|
|
670
|
+
bail = false) {
|
|
671
|
+
console.log("\n📡 Upgrading to realtime mode");
|
|
672
|
+
// Determine the environment for state update
|
|
673
|
+
const env = environment === "Development" ? "dev" : "production";
|
|
674
|
+
const upgradeResult = await page.evaluate(async () => {
|
|
675
|
+
try {
|
|
676
|
+
// Check if __rw API exists
|
|
677
|
+
if (typeof window.__rw !== "object" ||
|
|
678
|
+
typeof window.__rw.upgradeToRealtime !== "function") {
|
|
679
|
+
return {
|
|
680
|
+
success: false,
|
|
681
|
+
message: "The __rw API or upgradeToRealtime method is not available",
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
await window.__rw.upgradeToRealtime();
|
|
685
|
+
// If we get here, it succeeded
|
|
686
|
+
return { success: true };
|
|
687
|
+
}
|
|
688
|
+
catch (error) {
|
|
689
|
+
return {
|
|
690
|
+
success: false,
|
|
691
|
+
message: error instanceof Error ? error.message : String(error),
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
});
|
|
695
|
+
if (!upgradeResult.success) {
|
|
696
|
+
log("ERROR: Failed to upgrade to realtime mode: %s", upgradeResult.message);
|
|
697
|
+
const errorMessage = `Failed to upgrade to realtime mode: ${upgradeResult.message}`;
|
|
698
|
+
// Update test status to FAILED
|
|
699
|
+
updateTestStatus(env, "realtimeUpgrade", "FAILED");
|
|
700
|
+
if (bail) {
|
|
701
|
+
// If bail is true, call fail() which will exit the process
|
|
702
|
+
await fail(new Error(errorMessage), 1, `${environment} - Realtime Upgrade`);
|
|
703
|
+
return; // This will never be reached due to fail() exiting
|
|
704
|
+
}
|
|
705
|
+
else {
|
|
706
|
+
// Otherwise throw an error that can be caught by the caller
|
|
707
|
+
throw new Error(errorMessage);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
// Update test status to PASSED
|
|
711
|
+
updateTestStatus(env, "realtimeUpgrade", "PASSED");
|
|
712
|
+
console.log("✅ Successfully upgraded to realtime mode");
|
|
713
|
+
}
|
|
714
|
+
/**
|
|
715
|
+
* DRY: checkServerUp now checks both root and custom path if needed
|
|
716
|
+
*/
|
|
717
|
+
export async function checkServerUp(baseUrl, customPath = "/", retries = RETRIES, bail = false) {
|
|
718
|
+
// Always check root first, then custom path if different
|
|
719
|
+
const pathsToCheck = ["/"];
|
|
720
|
+
if (customPath !== "/" && customPath !== "") {
|
|
721
|
+
pathsToCheck.push(customPath);
|
|
722
|
+
}
|
|
723
|
+
for (const path of pathsToCheck) {
|
|
724
|
+
const url = baseUrl + (path.startsWith("/") ? path : "/" + path);
|
|
725
|
+
log("Checking if server is up at %s (max retries: %d)", url, retries);
|
|
726
|
+
let up = false;
|
|
727
|
+
for (let i = 0; i < retries; i++) {
|
|
728
|
+
try {
|
|
729
|
+
log("Attempt %d/%d to check server at %s", i + 1, retries, url);
|
|
730
|
+
console.log(`Checking if server is up at ${url} (attempt ${i + 1}/${retries})...`);
|
|
731
|
+
await $ `curl -s -o /dev/null -w "%{http_code}" ${url}`;
|
|
732
|
+
log("Server is up at %s", url);
|
|
733
|
+
console.log(`✅ Server is up at ${url}`);
|
|
734
|
+
up = true;
|
|
735
|
+
break;
|
|
736
|
+
}
|
|
737
|
+
catch (error) {
|
|
738
|
+
if (i === retries - 1) {
|
|
739
|
+
log("ERROR: Server at %s did not become available after %d attempts", url, retries);
|
|
740
|
+
const errorMessage = `Server at ${url} did not become available after ${retries} attempts`;
|
|
741
|
+
if (bail) {
|
|
742
|
+
// If bail is true, call fail() which will exit the process
|
|
743
|
+
await fail(new Error(errorMessage), 1, `Server Availability Check: ${url}`);
|
|
744
|
+
return false; // This will never be reached due to fail() exiting
|
|
745
|
+
}
|
|
746
|
+
else {
|
|
747
|
+
// Otherwise throw an error that can be caught by the caller
|
|
748
|
+
throw new Error(errorMessage);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
log("Server not up yet, retrying in 2 seconds");
|
|
752
|
+
console.log(`Server not up yet, retrying in 2 seconds...`);
|
|
753
|
+
await new Promise((resolve) => setTimeout(() => resolve(), 2000));
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
if (!up)
|
|
757
|
+
return false;
|
|
758
|
+
}
|
|
759
|
+
return true;
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* Perform only the realtime upgrade and tests without doing initial checks
|
|
763
|
+
*/
|
|
764
|
+
async function realtimeOnlyFlow(page, url, artifactDir, bail, skipClient, environment, targetDir, skipHmr = false) {
|
|
765
|
+
let hasFailures = false;
|
|
766
|
+
let realtimeError = null;
|
|
767
|
+
// Create timestamp state for the realtime-only flow
|
|
768
|
+
const timestampState = {
|
|
769
|
+
initialServerValue: 23, // This is the default initial module-level value
|
|
770
|
+
clientUpdatedValue: null,
|
|
771
|
+
};
|
|
772
|
+
try {
|
|
773
|
+
log("Performing realtime-only smoke test");
|
|
774
|
+
await checkUrlSmoke(page, url, true, bail, skipClient, environment, timestampState, targetDir, skipHmr);
|
|
775
|
+
// Take a screenshot of the realtime test
|
|
776
|
+
await takeScreenshot(page, url, artifactDir, `${environment.toLowerCase()}-realtime-passed`);
|
|
777
|
+
}
|
|
778
|
+
catch (error) {
|
|
779
|
+
hasFailures = true;
|
|
780
|
+
realtimeError = error instanceof Error ? error : new Error(String(error));
|
|
781
|
+
log("Error during realtime-only test: %O", error);
|
|
782
|
+
console.error(`❌ Realtime test failed: ${realtimeError.message}`);
|
|
783
|
+
// Take a failure screenshot
|
|
784
|
+
await takeScreenshot(page, url, artifactDir, `${environment.toLowerCase()}-realtime-failed`).catch((e) => log("Failed to take error screenshot: %O", e));
|
|
785
|
+
// If bail is true, propagate the error
|
|
786
|
+
if (bail) {
|
|
787
|
+
throw error;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
return { hasFailures, error: realtimeError };
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* HMR test for server component
|
|
794
|
+
* Updates the server component and verifies that HMR applies the changes
|
|
795
|
+
*/
|
|
796
|
+
export async function testServerComponentHmr(page, targetDir, phase = "", environment = "Development", bail = false) {
|
|
797
|
+
const testPhase = phase ? phase : "Initial";
|
|
798
|
+
// Skip HMR tests in production environments
|
|
799
|
+
if (environment !== "Development") {
|
|
800
|
+
console.log(`⏩ Skipping server HMR test in ${environment} environment`);
|
|
801
|
+
// Update test status to SKIPPED
|
|
802
|
+
const env = environment === "Development" ? "dev" : "production";
|
|
803
|
+
const testKey = testPhase === "Initial" || !testPhase
|
|
804
|
+
? "initialServerHmr"
|
|
805
|
+
: "realtimeServerHmr";
|
|
806
|
+
updateTestStatus(env, testKey, "SKIPPED");
|
|
807
|
+
return false;
|
|
808
|
+
}
|
|
809
|
+
console.log(`🔄 Testing ${testPhase} Server Component HMR`);
|
|
810
|
+
// Determine the environment and test key for state update
|
|
811
|
+
const env = environment === "Development" ? "dev" : "production";
|
|
812
|
+
const testKey = testPhase === "Initial" || !testPhase
|
|
813
|
+
? "initialServerHmr"
|
|
814
|
+
: "realtimeServerHmr";
|
|
815
|
+
try {
|
|
816
|
+
// First, verify the server HMR marker exists
|
|
817
|
+
log("Checking for server HMR marker");
|
|
818
|
+
const markerExists = await page.evaluate(() => {
|
|
819
|
+
const marker = document.querySelector('[data-testid="server-hmr-marker"]');
|
|
820
|
+
return !!marker;
|
|
821
|
+
});
|
|
822
|
+
if (!markerExists) {
|
|
823
|
+
log("Server HMR marker not found");
|
|
824
|
+
console.warn("⚠️ Server HMR marker not found in the page - skipping server HMR test");
|
|
825
|
+
updateTestStatus(env, testKey, "SKIPPED");
|
|
826
|
+
return false;
|
|
827
|
+
}
|
|
828
|
+
// Get the initial attributes before making changes
|
|
829
|
+
const initialAttributes = await page.evaluate(() => {
|
|
830
|
+
const marker = document.querySelector('[data-testid="server-hmr-marker"]');
|
|
831
|
+
if (!marker)
|
|
832
|
+
return null;
|
|
833
|
+
return {
|
|
834
|
+
text: marker.getAttribute("data-hmr-text"),
|
|
835
|
+
timestamp: marker.getAttribute("data-hmr-timestamp"),
|
|
836
|
+
content: marker.textContent,
|
|
837
|
+
};
|
|
838
|
+
});
|
|
839
|
+
log("Initial server HMR marker state: %O", initialAttributes);
|
|
840
|
+
// Find the SmokeTest.tsx file path
|
|
841
|
+
const smokePath = join(targetDir, "src", "app", "components", "__SmokeTest.tsx");
|
|
842
|
+
log("Looking for smoke test file at: %s", smokePath);
|
|
843
|
+
// Read the current file content
|
|
844
|
+
const fs = await import("fs/promises");
|
|
845
|
+
const fileContent = await fs.readFile(smokePath, "utf-8");
|
|
846
|
+
// Define the new content with updated HMR marker
|
|
847
|
+
const newTimestamp = Date.now();
|
|
848
|
+
const updatedContent = fileContent
|
|
849
|
+
.replace(/data-hmr-text="[^"]*"/g, `data-hmr-text="updated-${newTimestamp}"`)
|
|
850
|
+
.replace(/data-hmr-timestamp=\{[^}]*\}/g, `data-hmr-timestamp={${newTimestamp}}`)
|
|
851
|
+
.replace(/Server Component HMR: <span>[^<]*<\/span>/g, `Server Component HMR: <span>Updated Text ${newTimestamp}</span>`);
|
|
852
|
+
// Write the updated file
|
|
853
|
+
log("Writing updated server component content");
|
|
854
|
+
await fs.writeFile(smokePath, updatedContent, "utf-8");
|
|
855
|
+
// Wait for HMR to apply changes
|
|
856
|
+
console.log("Waiting for server HMR to update component...");
|
|
857
|
+
// Wait for the data-hmr-text attribute to change
|
|
858
|
+
log("Waiting for server HMR update to apply");
|
|
859
|
+
try {
|
|
860
|
+
await page.waitForFunction((timestamp) => {
|
|
861
|
+
const marker = document.querySelector('[data-testid="server-hmr-marker"]');
|
|
862
|
+
if (!marker)
|
|
863
|
+
return false;
|
|
864
|
+
const currentText = marker.getAttribute("data-hmr-text");
|
|
865
|
+
return (currentText &&
|
|
866
|
+
currentText.includes("updated-") &&
|
|
867
|
+
currentText.includes(timestamp));
|
|
868
|
+
}, { timeout: 10000 }, newTimestamp.toString());
|
|
869
|
+
log("Server HMR update detected");
|
|
870
|
+
}
|
|
871
|
+
catch (error) {
|
|
872
|
+
log("ERROR: Server HMR update not detected: %O", error);
|
|
873
|
+
updateTestStatus(env, testKey, "FAILED");
|
|
874
|
+
if (bail) {
|
|
875
|
+
await fail(new Error(`Server HMR test failed: Update not detected within timeout`), 1, `${environment} - Server HMR Test (${testPhase})`);
|
|
876
|
+
}
|
|
877
|
+
return false;
|
|
878
|
+
}
|
|
879
|
+
// Verify final state
|
|
880
|
+
const updatedAttributes = await page.evaluate(() => {
|
|
881
|
+
const marker = document.querySelector('[data-testid="server-hmr-marker"]');
|
|
882
|
+
if (!marker)
|
|
883
|
+
return null;
|
|
884
|
+
return {
|
|
885
|
+
text: marker.getAttribute("data-hmr-text"),
|
|
886
|
+
timestamp: marker.getAttribute("data-hmr-timestamp"),
|
|
887
|
+
content: marker.textContent,
|
|
888
|
+
};
|
|
889
|
+
});
|
|
890
|
+
log("Updated server HMR marker state: %O", updatedAttributes);
|
|
891
|
+
const hmrSuccess = updatedAttributes &&
|
|
892
|
+
updatedAttributes.text &&
|
|
893
|
+
updatedAttributes.text.includes("updated-");
|
|
894
|
+
updateTestStatus(env, testKey, hmrSuccess ? "PASSED" : "FAILED");
|
|
895
|
+
if (hmrSuccess) {
|
|
896
|
+
console.log("✅ Server component HMR test passed");
|
|
897
|
+
return true;
|
|
898
|
+
}
|
|
899
|
+
else {
|
|
900
|
+
console.error("❌ Server component HMR test failed: Content did not update properly");
|
|
901
|
+
if (bail) {
|
|
902
|
+
await fail(new Error(`Server HMR test failed: Content did not update properly`), 1, `${environment} - Server HMR Test (${testPhase})`);
|
|
903
|
+
}
|
|
904
|
+
return false;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
catch (error) {
|
|
908
|
+
log("Error during server HMR test: %O", error);
|
|
909
|
+
console.error(`❌ Server HMR test failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
910
|
+
updateTestStatus(env, testKey, "FAILED");
|
|
911
|
+
if (bail) {
|
|
912
|
+
await fail(error instanceof Error ? error : new Error(String(error)), 1, `${environment} - Server HMR Test (${testPhase})`);
|
|
913
|
+
}
|
|
914
|
+
return false;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
/**
|
|
918
|
+
* HMR test for client component
|
|
919
|
+
* Updates the client component and verifies that HMR applies the changes
|
|
920
|
+
*/
|
|
921
|
+
export async function testClientComponentHmr(page, targetDir, phase = "", environment = "Development", bail = false) {
|
|
922
|
+
const testPhase = phase ? phase : "Initial";
|
|
923
|
+
// Skip HMR tests in production environments
|
|
924
|
+
if (environment !== "Development") {
|
|
925
|
+
console.log(`⏩ Skipping client HMR test in ${environment} environment`);
|
|
926
|
+
// Update test status to SKIPPED
|
|
927
|
+
const env = environment === "Development" ? "dev" : "production";
|
|
928
|
+
const testKey = testPhase === "Initial" || !testPhase
|
|
929
|
+
? "initialClientHmr"
|
|
930
|
+
: "realtimeClientHmr";
|
|
931
|
+
updateTestStatus(env, testKey, "SKIPPED");
|
|
932
|
+
return false;
|
|
933
|
+
}
|
|
934
|
+
console.log(`🔄 Testing ${testPhase} Client Component HMR`);
|
|
935
|
+
// Determine the environment and test key for state update
|
|
936
|
+
const env = environment === "Development" ? "dev" : "production";
|
|
937
|
+
const testKey = testPhase === "Initial" || !testPhase
|
|
938
|
+
? "initialClientHmr"
|
|
939
|
+
: "realtimeClientHmr";
|
|
940
|
+
try {
|
|
941
|
+
// First, verify the client HMR marker exists
|
|
942
|
+
log("Checking for client HMR marker");
|
|
943
|
+
const markerExists = await page.evaluate(() => {
|
|
944
|
+
const marker = document.querySelector('[data-testid="client-hmr-marker"]');
|
|
945
|
+
return !!marker;
|
|
946
|
+
});
|
|
947
|
+
if (!markerExists) {
|
|
948
|
+
log("Client HMR marker not found");
|
|
949
|
+
console.warn("⚠️ Client HMR marker not found in the page - skipping client HMR test");
|
|
950
|
+
updateTestStatus(env, testKey, "SKIPPED");
|
|
951
|
+
return false;
|
|
952
|
+
}
|
|
953
|
+
// Get the initial attributes before making changes
|
|
954
|
+
const initialAttributes = await page.evaluate(() => {
|
|
955
|
+
const marker = document.querySelector('[data-testid="client-hmr-marker"]');
|
|
956
|
+
if (!marker)
|
|
957
|
+
return null;
|
|
958
|
+
return {
|
|
959
|
+
text: marker.getAttribute("data-hmr-text"),
|
|
960
|
+
timestamp: marker.getAttribute("data-hmr-timestamp"),
|
|
961
|
+
content: marker.textContent,
|
|
962
|
+
};
|
|
963
|
+
});
|
|
964
|
+
log("Initial client HMR marker state: %O", initialAttributes);
|
|
965
|
+
// Find the SmokeTestClient.tsx file path
|
|
966
|
+
const clientPath = join(targetDir, "src", "app", "components", "__SmokeTestClient.tsx");
|
|
967
|
+
log("Looking for client component file at: %s", clientPath);
|
|
968
|
+
// Read the current file content
|
|
969
|
+
const fs = await import("fs/promises");
|
|
970
|
+
const fileContent = await fs.readFile(clientPath, "utf-8");
|
|
971
|
+
// Define the new content with updated HMR marker
|
|
972
|
+
const newTimestamp = Date.now();
|
|
973
|
+
const updatedContent = fileContent
|
|
974
|
+
.replace(/data-hmr-text="[^"]*"/g, `data-hmr-text="updated-${newTimestamp}"`)
|
|
975
|
+
.replace(/data-hmr-timestamp=\{[^}]*\}/g, `data-hmr-timestamp={${newTimestamp}}`)
|
|
976
|
+
.replace(/Client Component HMR: <span>[^<]*<\/span>/g, `Client Component HMR: <span>Updated Text ${newTimestamp}</span>`);
|
|
977
|
+
// Write the updated file
|
|
978
|
+
log("Writing updated client component content");
|
|
979
|
+
await fs.writeFile(clientPath, updatedContent, "utf-8");
|
|
980
|
+
// Wait for HMR to apply changes
|
|
981
|
+
console.log("Waiting for client HMR to update component...");
|
|
982
|
+
// Wait for the data-hmr-text attribute to change
|
|
983
|
+
log("Waiting for client HMR update to apply");
|
|
984
|
+
try {
|
|
985
|
+
await page.waitForFunction((timestamp) => {
|
|
986
|
+
const marker = document.querySelector('[data-testid="client-hmr-marker"]');
|
|
987
|
+
if (!marker)
|
|
988
|
+
return false;
|
|
989
|
+
const currentText = marker.getAttribute("data-hmr-text");
|
|
990
|
+
return (currentText &&
|
|
991
|
+
currentText.includes("updated-") &&
|
|
992
|
+
currentText.includes(timestamp));
|
|
993
|
+
}, { timeout: 10000 }, newTimestamp.toString());
|
|
994
|
+
log("Client HMR update detected");
|
|
995
|
+
}
|
|
996
|
+
catch (error) {
|
|
997
|
+
log("ERROR: Client HMR update not detected: %O", error);
|
|
998
|
+
updateTestStatus(env, testKey, "FAILED");
|
|
999
|
+
if (bail) {
|
|
1000
|
+
await fail(new Error(`Client HMR test failed: Update not detected within timeout`), 1, `${environment} - Client HMR Test (${testPhase})`);
|
|
1001
|
+
}
|
|
1002
|
+
return false;
|
|
1003
|
+
}
|
|
1004
|
+
// Verify final state
|
|
1005
|
+
const updatedAttributes = await page.evaluate(() => {
|
|
1006
|
+
const marker = document.querySelector('[data-testid="client-hmr-marker"]');
|
|
1007
|
+
if (!marker)
|
|
1008
|
+
return null;
|
|
1009
|
+
return {
|
|
1010
|
+
text: marker.getAttribute("data-hmr-text"),
|
|
1011
|
+
timestamp: marker.getAttribute("data-hmr-timestamp"),
|
|
1012
|
+
content: marker.textContent,
|
|
1013
|
+
};
|
|
1014
|
+
});
|
|
1015
|
+
log("Updated client HMR marker state: %O", updatedAttributes);
|
|
1016
|
+
const hmrSuccess = updatedAttributes &&
|
|
1017
|
+
updatedAttributes.text &&
|
|
1018
|
+
updatedAttributes.text.includes("updated-");
|
|
1019
|
+
updateTestStatus(env, testKey, hmrSuccess ? "PASSED" : "FAILED");
|
|
1020
|
+
if (hmrSuccess) {
|
|
1021
|
+
console.log("✅ Client component HMR test passed");
|
|
1022
|
+
return true;
|
|
1023
|
+
}
|
|
1024
|
+
else {
|
|
1025
|
+
console.error("❌ Client component HMR test failed: Content did not update properly");
|
|
1026
|
+
if (bail) {
|
|
1027
|
+
await fail(new Error(`Client HMR test failed: Content did not update properly`), 1, `${environment} - Client HMR Test (${testPhase})`);
|
|
1028
|
+
}
|
|
1029
|
+
return false;
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
catch (error) {
|
|
1033
|
+
log("Error during client HMR test: %O", error);
|
|
1034
|
+
console.error(`❌ Client HMR test failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1035
|
+
updateTestStatus(env, testKey, "FAILED");
|
|
1036
|
+
if (bail) {
|
|
1037
|
+
await fail(error instanceof Error ? error : new Error(String(error)), 1, `${environment} - Client HMR Test (${testPhase})`);
|
|
1038
|
+
}
|
|
1039
|
+
return false;
|
|
1040
|
+
}
|
|
1041
|
+
}
|