diff --git a/.github/workflows/ci-validate-platforms.yml b/.github/workflows/ci-validate-platforms.yml index 73aa4feddaf..ccc4249c754 100644 --- a/.github/workflows/ci-validate-platforms.yml +++ b/.github/workflows/ci-validate-platforms.yml @@ -62,7 +62,7 @@ jobs: - name: Install playwright dependencies and browsers run: | - npx playwright install + npx playwright install --with-deps - name: Run tests in all Packages run: npm run test diff --git a/.github/workflows/ci-validate-pr.yml b/.github/workflows/ci-validate-pr.yml index 4f061f8ab6d..620a0254649 100644 --- a/.github/workflows/ci-validate-pr.yml +++ b/.github/workflows/ci-validate-pr.yml @@ -49,7 +49,7 @@ jobs: - name: Install playwright dependencies and browsers run: | - npx playwright install + npx playwright install --with-deps - name: Testing unit tests run: npm run test diff --git a/change/@microsoft-fast-element-70d07072-14fe-484f-817c-1ca791c5f9ba.json b/change/@microsoft-fast-element-70d07072-14fe-484f-817c-1ca791c5f9ba.json new file mode 100644 index 00000000000..1449005f7cd --- /dev/null +++ b/change/@microsoft-fast-element-70d07072-14fe-484f-817c-1ca791c5f9ba.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "feat(hydration): add hydrationStarted callback and update documentation", + "packageName": "@microsoft/fast-element", + "email": "863023+radium-v@users.noreply.github.com", + "dependentChangeType": "none" +} diff --git a/change/@microsoft-fast-html-1e35899c-c956-4da3-8da6-b6400d330580.json b/change/@microsoft-fast-html-1e35899c-c956-4da3-8da6-b6400d330580.json new file mode 100644 index 00000000000..1bae0b196d2 --- /dev/null +++ b/change/@microsoft-fast-html-1e35899c-c956-4da3-8da6-b6400d330580.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "test(performance-metrics): enhance performance tracking and add hydration metrics", + "packageName": "@microsoft/fast-html", + "email": "863023+radium-v@users.noreply.github.com", + "dependentChangeType": "none" +} diff --git a/packages/fast-element/docs/api-report.api.md b/packages/fast-element/docs/api-report.api.md index 64499803df7..986594dec9c 100644 --- a/packages/fast-element/docs/api-report.api.md +++ b/packages/fast-element/docs/api-report.api.md @@ -631,6 +631,7 @@ export interface HydrationControllerCallbacks { /** - * Called before hydration has started + * Called once when the first element enters the hydration pipeline. + * This is the earliest point at which we know a component has been + * async-defined with `defer-and-hydrate`, a template is pending via + * ``, and the element has `needs-hydration`. + */ + hydrationStarted?(): void; + + /** + * Called before an individual element's hydration begins */ elementWillHydrate?(source: TElement): void; /** - * Called after hydration has finished + * Called after an individual element's hydration has finished */ elementDidHydrate?(source: TElement): void; @@ -887,6 +895,11 @@ export class HydratableElementController< */ public static lifecycleCallbacks: HydrationControllerCallbacks = {}; + /** + * Whether the hydrationStarted callback has already been invoked. + */ + private static hydrationStarted: boolean = false; + /** * An idle callback ID used to track hydration completion */ @@ -946,7 +959,11 @@ export class HydratableElementController< // If there are no more hydrating instances, invoke the hydrationComplete callback if (HydratableElementController.hydratingInstances?.size === 0) { - HydratableElementController.notifyHydrationComplete(); + try { + HydratableElementController.lifecycleCallbacks.hydrationComplete?.(); + } catch { + // A lifecycle callback must never prevent post-hydration cleanup. + } // Reset to the default strategy after hydration is complete ElementController.setStrategy(ElementController); @@ -986,7 +1003,23 @@ export class HydratableElementController< return; } - this.notifyWillHydrate(); + if (!HydratableElementController.hydrationStarted) { + HydratableElementController.hydrationStarted = true; + + try { + HydratableElementController.lifecycleCallbacks.hydrationStarted?.(); + } catch { + // A lifecycle callback must never prevent hydration. + } + } + + try { + HydratableElementController.lifecycleCallbacks.elementWillHydrate?.( + this.source + ); + } catch { + // A lifecycle callback must never prevent hydration. + } this.stage = Stages.connecting; @@ -1053,11 +1086,17 @@ export class HydratableElementController< return; } + try { + HydratableElementController.lifecycleCallbacks.elementDidHydrate?.( + this.source + ); + } catch { + // A lifecycle callback must never prevent hydration. + } + const name = this.definition.name; const instances = HydratableElementController.hydratingInstances.get(name); - this.notifyDidHydrate(); - if (instances) { instances.delete(this.source); @@ -1076,55 +1115,6 @@ export class HydratableElementController< } } - /** - * Notifies that hydration is about to start for this element. - * Safely invokes the configured elementWillHydrate callback, if any. - */ - private notifyWillHydrate(): void { - const callback = - HydratableElementController.lifecycleCallbacks.elementWillHydrate; - - if (callback) { - try { - callback(this.source); - } catch { - // A lifecycle callback must never prevent hydration. - } - } - } - - /** - * Notifies that hydration has finished for this element. - * Safely invokes the configured elementDidHydrate callback, if any. - */ - private notifyDidHydrate(): void { - const callback = HydratableElementController.lifecycleCallbacks.elementDidHydrate; - - if (callback) { - try { - callback(this.source); - } catch { - // A lifecycle callback must never prevent hydration. - } - } - } - - /** - * Notifies that all elements have completed hydration. - * Safely invokes the configured hydrationComplete callback, if any. - */ - private static notifyHydrationComplete(): void { - const callback = HydratableElementController.lifecycleCallbacks.hydrationComplete; - - if (callback) { - try { - callback(); - } catch { - // A lifecycle callback must never prevent post-hydration cleanup. - } - } - } - /** * Unregisters the hydration observer when the element is disconnected. */ diff --git a/packages/fast-html/playwright.config.ts b/packages/fast-html/playwright.config.ts index cc5227dbd6b..3328388a589 100644 --- a/packages/fast-html/playwright.config.ts +++ b/packages/fast-html/playwright.config.ts @@ -1,9 +1,14 @@ -import { defineConfig } from "@playwright/test"; +import { defineConfig, devices } from "@playwright/test"; export default defineConfig({ testDir: ".", testMatch: "**/*.spec.ts", retries: 3, + projects: [ + { name: "chromium", use: { ...devices["Desktop Chrome"] } }, + { name: "firefox", use: { ...devices["Desktop Firefox"] } }, + { name: "webkit", use: { ...devices["Desktop Safari"] } }, + ], webServer: { command: "npm run test-server", port: 5173, diff --git a/packages/fast-html/test/fixtures/performance-metrics/fast-card.css b/packages/fast-html/test/fixtures/performance-metrics/fast-card.css index b010b61ae08..8215ab5168e 100644 --- a/packages/fast-html/test/fixtures/performance-metrics/fast-card.css +++ b/packages/fast-html/test/fixtures/performance-metrics/fast-card.css @@ -3,4 +3,8 @@ width: 200px; height: 200px; border: 1px solid #ccc; + + dd { + display: inline-block; + } } diff --git a/packages/fast-html/test/fixtures/performance-metrics/index.html b/packages/fast-html/test/fixtures/performance-metrics/index.html index 9086294f970..a8ada05573a 100644 --- a/packages/fast-html/test/fixtures/performance-metrics/index.html +++ b/packages/fast-html/test/fixtures/performance-metrics/index.html @@ -3,9 +3,10 @@ Performance Metrics Test + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +