diff --git a/packages/core/src/compiler/output-targets/standalone/_test_/standalone-types.spec.ts b/packages/core/src/compiler/output-targets/standalone/_test_/standalone-types.spec.ts
new file mode 100644
index 00000000000..35c560b2728
--- /dev/null
+++ b/packages/core/src/compiler/output-targets/standalone/_test_/standalone-types.spec.ts
@@ -0,0 +1,18 @@
+import { describe, expect, it } from 'vitest';
+
+import { stubComponentCompilerMeta } from '../../../types/_tests_/ComponentCompilerMeta.stub';
+import { generateCustomElementType } from '../standalone-types';
+
+describe('generateCustomElementType', () => {
+ it('extends HTMLStencilElement so componentOnReady() is typed', () => {
+ const cmpMeta = stubComponentCompilerMeta({ tagName: 'my-button' });
+
+ const result = generateCustomElementType('../types/components', cmpMeta);
+
+ expect(result).toContain('import type { HTMLStencilElement } from "@stencil/core/runtime";');
+ expect(result).toContain(
+ 'interface MyButton extends Components.MyButton, HTMLStencilElement {}',
+ );
+ expect(result).not.toContain('HTMLElement {}');
+ });
+});
diff --git a/packages/core/src/compiler/output-targets/standalone/standalone-types.ts b/packages/core/src/compiler/output-targets/standalone/standalone-types.ts
index 3d46cc21f37..191cfbf194c 100644
--- a/packages/core/src/compiler/output-targets/standalone/standalone-types.ts
+++ b/packages/core/src/compiler/output-targets/standalone/standalone-types.ts
@@ -195,15 +195,16 @@ const generateLoaderType = (): string => {
* @param cmp the component to generate the type declaration file for
* @returns the contents of the type declaration file for the provided `cmp`
*/
-const generateCustomElementType = (
+export const generateCustomElementType = (
componentsDtsRelPath: string,
cmp: d.ComponentCompilerMeta,
): string => {
const tagNameAsPascal = dashToPascalCase(cmp.tagName);
const o: string[] = [
`import type { Components, JSX } from "${componentsDtsRelPath}";`,
+ `import type { HTMLStencilElement } from "@stencil/core/runtime";`,
``,
- `interface ${tagNameAsPascal} extends Components.${tagNameAsPascal}, HTMLElement {}`,
+ `interface ${tagNameAsPascal} extends Components.${tagNameAsPascal}, HTMLStencilElement {}`,
`export const ${tagNameAsPascal}: {`,
` prototype: ${tagNameAsPascal};`,
` new (): ${tagNameAsPascal};`,
diff --git a/test/runtime/src/components/lifecycle/lifecycle-basic/lifecycle-basic.spec.tsx b/test/runtime/src/components/lifecycle/lifecycle-basic/lifecycle-basic.spec.tsx
index 5a56776dec5..2be8e76cb8b 100644
--- a/test/runtime/src/components/lifecycle/lifecycle-basic/lifecycle-basic.spec.tsx
+++ b/test/runtime/src/components/lifecycle/lifecycle-basic/lifecycle-basic.spec.tsx
@@ -38,4 +38,17 @@ describe('lifecycle-basic', () => {
expect(updates[4]).toHaveTextContent('componentDidUpdate-b');
expect(updates[5]).toHaveTextContent('componentDidUpdate-a');
});
+
+ it('componentOnReady does not resolve until nested children have finished loading', async () => {
+ const { root } = await render(, { waitForReady: false });
+
+ await customElements.whenDefined('lifecycle-basic-a');
+ await (root as any).componentOnReady();
+
+ const b = root.querySelector('lifecycle-basic-b')!;
+ const c = root.querySelector('lifecycle-basic-c')!;
+
+ expect(b.classList.contains('hydrated')).toBe(true);
+ expect(c.classList.contains('hydrated')).toBe(true);
+ });
});
diff --git a/test/runtime/src/components/lifecycle/lifecycle-nested/lifecycle-nested.spec.tsx b/test/runtime/src/components/lifecycle/lifecycle-nested/lifecycle-nested.spec.tsx
index f4b19746953..1c124e5d74a 100644
--- a/test/runtime/src/components/lifecycle/lifecycle-nested/lifecycle-nested.spec.tsx
+++ b/test/runtime/src/components/lifecycle/lifecycle-nested/lifecycle-nested.spec.tsx
@@ -25,4 +25,20 @@ describe('lifecycle-nested', () => {
expect(loads[4]).toHaveTextContent('componentDidLoad-b');
expect(loads[5]).toHaveTextContent('componentDidLoad-c');
});
+
+ it('componentOnReady on the outermost element waits for slotted nested children', async () => {
+ const { root } = await render(
+ '',
+ { waitForReady: false },
+ );
+
+ await customElements.whenDefined('lifecycle-nested-c');
+ await (root as any).componentOnReady();
+
+ const b = root.querySelector('lifecycle-nested-b')!;
+ const a = root.querySelector('lifecycle-nested-a')!;
+
+ expect(b.classList.contains('hydrated')).toBe(true);
+ expect(a.classList.contains('hydrated')).toBe(true);
+ });
});
diff --git a/test/runtime/vitest.config.ts b/test/runtime/vitest.config.ts
index 5638ae55470..6dfda5242fa 100644
--- a/test/runtime/vitest.config.ts
+++ b/test/runtime/vitest.config.ts
@@ -12,6 +12,7 @@ export default defineVitestConfig({
include: ['src/**/*.spec.{ts,tsx}'],
exclude: ['src/scer/**'],
setupFiles: ['./vitest-setup-dist.ts'],
+ retry: 2,
env: {
TEST_PROJECT: 'dist',
},
@@ -30,6 +31,7 @@ export default defineVitestConfig({
include: ['src/**/*.spec.{ts,tsx}'],
exclude: ['src/scer/**'],
setupFiles: ['./vitest-setup-custom-elements.ts'],
+ retry: 2,
env: {
TEST_PROJECT: 'custom-elements',
},
@@ -47,6 +49,7 @@ export default defineVitestConfig({
name: 'scer-lazy',
include: ['src/scer/**/*.spec.{ts,tsx}'],
setupFiles: ['./vitest-setup-scer-lazy.ts'],
+ retry: 2,
browser: {
enabled: true,
provider: playwright(),
@@ -61,6 +64,7 @@ export default defineVitestConfig({
name: 'scer-standalone',
include: ['src/scer/**/*.spec.{ts,tsx}'],
setupFiles: ['./vitest-setup-scer-standalone.ts'],
+ retry: 2,
browser: {
enabled: true,
provider: playwright(),