diff --git a/packages/catalog-realm/package.json b/packages/catalog-realm/package.json index 670cf126375..0511e2c080f 100644 --- a/packages/catalog-realm/package.json +++ b/packages/catalog-realm/package.json @@ -8,7 +8,10 @@ "@cardstack/boxel-ui": "workspace:*", "@cardstack/local-types": "workspace:*", "@cardstack/runtime-common": "workspace:*", + "@ember/test-helpers": "catalog:", "@types/lodash": "catalog:", + "@types/qunit": "catalog:", + "@universal-ember/test-support": "catalog:", "@types/uuid": "catalog:", "chess.js": "catalog:", "concurrently": "catalog:", diff --git a/packages/catalog-realm/tsconfig.json b/packages/catalog-realm/tsconfig.json index bd925f1b7f3..e18beb2caa5 100644 --- a/packages/catalog-realm/tsconfig.json +++ b/packages/catalog-realm/tsconfig.json @@ -27,6 +27,7 @@ }, "types": ["@cardstack/local-types"] }, + "include": ["**/*.ts", "**/*.gts", "../local-types/test-support.d.ts"], "glint": { "environment": ["ember-loose", "ember-template-imports"] } diff --git a/packages/experiments-realm/package.json b/packages/experiments-realm/package.json index 4be0b2488a7..48903a065b4 100644 --- a/packages/experiments-realm/package.json +++ b/packages/experiments-realm/package.json @@ -8,6 +8,9 @@ "@cardstack/boxel-ui": "workspace:*", "@cardstack/local-types": "workspace:*", "@cardstack/runtime-common": "workspace:*", + "@ember/test-helpers": "catalog:", + "@types/qunit": "catalog:", + "@universal-ember/test-support": "catalog:", "@cardstack/view-transitions": "catalog:", "@types/lodash": "catalog:", "ember-animated": "catalog:", diff --git a/packages/experiments-realm/sample-command-card.gts b/packages/experiments-realm/sample-command-card.gts new file mode 100644 index 00000000000..85d9796ef7c --- /dev/null +++ b/packages/experiments-realm/sample-command-card.gts @@ -0,0 +1,106 @@ +import { CardDef, field, contains, Component } from 'https://cardstack.com/base/card-api'; +import StringField from 'https://cardstack.com/base/string'; + +export class SampleCommandCard extends CardDef { + static displayName = 'Sample Command Card'; + @field title = contains(StringField); + + static isolated = class Isolated extends Component { + + }; +} + +// ── Tests (imports resolved via loader.shimModule in live-test.js) ──────────── +import { on } from '@ember/modifier'; +import { service } from '@ember/service'; +import { click, render } from '@ember/test-helpers'; +import GlimmerComponent from '@glimmer/component'; +import { module, test } from 'qunit'; + +import { getService } from '@universal-ember/test-support'; +import { baseRealm } from '@cardstack/runtime-common'; +import type StoreService from '@cardstack/host/services/store'; +import { + setupIntegrationTestRealm, + setupLocalIndexing, + setupOnSave, + setupCardLogs, + testRealmURL, + setupRealmCacheTeardown, + withCachedRealmSetup, + type TestContextWithSave, +} from '@cardstack/host/tests/helpers'; +import { setupMockMatrix } from '@cardstack/host/tests/helpers/mock-matrix'; +import { setupRenderingTest } from '@cardstack/host/tests/helpers/setup'; +import type { TestRealmAdapter } from '@cardstack/host/tests/helpers/adapter'; + +class CreateCardButton extends GlimmerComponent { + @service declare store: StoreService; + + createCard = async () => { + await this.store.add(new SampleCommandCard({ title: 'Hello from live-test' })); + }; + + +} + +module('Experiments | SampleCommandCard', function (hooks) { + setupRenderingTest(hooks); + setupLocalIndexing(hooks); + setupOnSave(hooks); + setupRealmCacheTeardown(hooks); + setupCardLogs(hooks, async () => + (getService('loader-service') as any).loader.import(`${baseRealm.url}card-api`), + ); + + let mockMatrixUtils = setupMockMatrix(hooks, { + loggedInAs: '@testuser:localhost', + activeRealms: [testRealmURL], + autostart: true, + }); + + let testRealmAdapter: TestRealmAdapter; + + hooks.beforeEach(async function () { + ({ adapter: testRealmAdapter } = await withCachedRealmSetup(async () => + setupIntegrationTestRealm({ + mockMatrixUtils, + contents: { + 'sample-command-card.gts': { SampleCommandCard }, + '.realm.json': '{ "name": "Sample Realm" }', + }, + }), + )); + }); + + test('clicking Create Card writes a new card to the realm', async function ( + this: TestContextWithSave, + assert, + ) { + assert.expect(3); + + let savedUrl: URL | undefined; + this.onSave((url, doc) => { + savedUrl = url; + assert.strictEqual( + (doc as any).data.attributes.title, + 'Hello from live-test', + 'saved doc has correct title', + ); + }); + + await render(); + await click('button'); + + assert.ok(savedUrl, 'card was saved to realm'); + let relativePath = `${savedUrl!.href.substring(testRealmURL.length)}.json`; + let file = await testRealmAdapter.openFile(relativePath); + assert.ok(file, 'card JSON file exists in the realm adapter'); + }, + ); +}); diff --git a/packages/experiments-realm/tsconfig.json b/packages/experiments-realm/tsconfig.json index f1208dafd2c..8bc7ca601b8 100644 --- a/packages/experiments-realm/tsconfig.json +++ b/packages/experiments-realm/tsconfig.json @@ -28,6 +28,7 @@ }, "types": ["@cardstack/local-types"] }, + "include": ["**/*.ts", "**/*.gts", "../local-types/test-support.d.ts"], "glint": { "environment": ["ember-loose", "ember-template-imports"] } diff --git a/packages/host/package.json b/packages/host/package.json index 5476e470286..576f160bee0 100644 --- a/packages/host/package.json +++ b/packages/host/package.json @@ -48,7 +48,7 @@ "@cardstack/view-transitions": "catalog:", "@ember/optional-features": "^2.0.0", "@ember/string": "^3.1.1", - "@ember/test-helpers": "^3.3.1", + "@ember/test-helpers": "catalog:", "@ember/test-waiters": "catalog:", "@embroider/compat": "^3.5.5", "@embroider/core": "^3.4.15", diff --git a/packages/local-types/test-support.d.ts b/packages/local-types/test-support.d.ts new file mode 100644 index 00000000000..816ce4768e3 --- /dev/null +++ b/packages/local-types/test-support.d.ts @@ -0,0 +1,66 @@ +// Opt-in type declarations for colocated realm test files. +// Add this path to your realm package's tsconfig "include" array: +// "../local-types/test-support.d.ts" +// +// Also add to your realm package's devDependencies: +// "@ember/test-helpers": "catalog:" +// "@types/qunit": "catalog:" +// "@universal-ember/test-support": "catalog:" +// +// The declarations below cover @cardstack/host internal modules which have no +// separate npm package. All other test imports resolve via node_modules. + +// ── @cardstack/host services ───────────────────────────────────────────────── +declare module '@cardstack/host/services/store' { + export default class StoreService { + add(instance: unknown): Promise; + peek(id: string): unknown; + } +} + +// ── @cardstack/host test helpers ───────────────────────────────────────────── +declare module '@cardstack/host/tests/helpers/adapter' { + export interface TestRealmAdapter { + openFile(path: string): Promise<{ content: string | Uint8Array } | undefined>; + } +} + +declare module '@cardstack/host/tests/helpers' { + import type { TestRealmAdapter } from '@cardstack/host/tests/helpers/adapter'; + + export interface SetupRealmResult { + adapter: TestRealmAdapter; + } + + export function setupIntegrationTestRealm(opts: { + mockMatrixUtils: unknown; + contents: Record; + }): Promise; + + export function setupLocalIndexing(hooks: unknown): void; + export function setupOnSave(hooks: unknown): void; + export function setupCardLogs(hooks: unknown, fn: () => Promise): void; + export function setupRealmCacheTeardown(hooks: unknown): void; + export function withCachedRealmSetup(fn: () => Promise): Promise; + + export const testRealmURL: string; + + export interface TestContextWithSave { + onSave(cb: (url: URL, doc: unknown) => void): void; + } +} + +declare module '@cardstack/host/tests/helpers/mock-matrix' { + export function setupMockMatrix( + hooks: unknown, + opts: { + loggedInAs: string; + activeRealms: string[]; + autostart: boolean; + }, + ): unknown; +} + +declare module '@cardstack/host/tests/helpers/setup' { + export function setupRenderingTest(hooks: unknown): void; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 68842cb5750..63019e54fdf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -75,6 +75,9 @@ catalogs: '@ember/string': specifier: ^4.0.1 version: 4.0.1 + '@ember/test-helpers': + specifier: ^3.3.1 + version: 3.3.1 '@ember/test-waiters': specifier: ^4.1.1 version: 4.1.1 @@ -734,7 +737,7 @@ importers: version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.12.0(patch_hash=2586bd032e74105d65b7953ccaae1cd1d1175047a58ba89ec0057f1f8b7b7fe1)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))) '@glint/environment-ember-template-imports': specifier: 1.3.0 - version: 1.3.0(@glint/environment-ember-loose@1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.12.0(patch_hash=2586bd032e74105d65b7953ccaae1cd1d1175047a58ba89ec0057f1f8b7b7fe1)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))))(@glint/template@1.3.0) + version: 1.3.0(@glint/environment-ember-loose@1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.12.0(patch_hash=2586bd032e74105d65b7953ccaae1cd1d1175047a58ba89ec0057f1f8b7b7fe1)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))))(@glint/template@1.3.0) '@playwright/test': specifier: 'catalog:' version: 1.57.0 @@ -1391,7 +1394,7 @@ importers: version: 1.3.0(typescript@5.9.3) '@glint/environment-ember-template-imports': specifier: 1.3.0 - version: 1.3.0(@glint/environment-ember-loose@1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.12.0(patch_hash=2586bd032e74105d65b7953ccaae1cd1d1175047a58ba89ec0057f1f8b7b7fe1)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))))(@glint/template@1.3.0) + version: 1.3.0(@glint/environment-ember-loose@1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.12.0(patch_hash=2586bd032e74105d65b7953ccaae1cd1d1175047a58ba89ec0057f1f8b7b7fe1)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))))(@glint/template@1.3.0) '@glint/template': specifier: 1.3.0 version: 1.3.0 @@ -1586,12 +1589,21 @@ importers: '@cardstack/runtime-common': specifier: workspace:* version: link:../runtime-common + '@ember/test-helpers': + specifier: 'catalog:' + version: 3.3.1(@babel/core@7.28.6)(@glint/template@1.3.0)(ember-source@5.12.0(patch_hash=2586bd032e74105d65b7953ccaae1cd1d1175047a58ba89ec0057f1f8b7b7fe1)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))(webpack@5.104.1) '@types/lodash': specifier: 'catalog:' version: 4.17.23 + '@types/qunit': + specifier: 'catalog:' + version: 2.19.13 '@types/uuid': specifier: 'catalog:' version: 9.0.8 + '@universal-ember/test-support': + specifier: 'catalog:' + version: 0.5.1(@babel/core@7.28.6)(@glint/template@1.3.0)(ember-source@5.12.0(patch_hash=2586bd032e74105d65b7953ccaae1cd1d1175047a58ba89ec0057f1f8b7b7fe1)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))(qunit@2.25.0) chess.js: specifier: 'catalog:' version: 1.0.0-beta.8 @@ -1704,9 +1716,18 @@ importers: '@cardstack/view-transitions': specifier: 'catalog:' version: 0.2.0(@babel/core@7.28.6)(ember-modifier@4.1.0(ember-source@5.12.0(patch_hash=2586bd032e74105d65b7953ccaae1cd1d1175047a58ba89ec0057f1f8b7b7fe1)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))) + '@ember/test-helpers': + specifier: 'catalog:' + version: 3.3.1(@babel/core@7.28.6)(@glint/template@1.3.0)(ember-source@5.12.0(patch_hash=2586bd032e74105d65b7953ccaae1cd1d1175047a58ba89ec0057f1f8b7b7fe1)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))(webpack@5.104.1) '@types/lodash': specifier: 'catalog:' version: 4.17.23 + '@types/qunit': + specifier: 'catalog:' + version: 2.19.13 + '@universal-ember/test-support': + specifier: 'catalog:' + version: 0.5.1(@babel/core@7.28.6)(@glint/template@1.3.0)(ember-source@5.12.0(patch_hash=2586bd032e74105d65b7953ccaae1cd1d1175047a58ba89ec0057f1f8b7b7fe1)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))(qunit@2.25.0) chess.js: specifier: 'catalog:' version: 1.0.0-beta.8 @@ -1715,7 +1736,7 @@ importers: version: 8.2.2 ember-animated: specifier: 'catalog:' - version: 2.2.0(@babel/core@7.28.6)(@ember/test-helpers@5.4.1(@babel/core@7.28.6)(@glint/template@1.3.0))(@glint/template@1.3.0) + version: 2.2.0(@babel/core@7.28.6)(@ember/test-helpers@3.3.1(@babel/core@7.28.6)(@glint/template@1.3.0)(ember-source@5.12.0(patch_hash=2586bd032e74105d65b7953ccaae1cd1d1175047a58ba89ec0057f1f8b7b7fe1)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))(webpack@5.104.1))(@glint/template@1.3.0) ember-concurrency: specifier: 'catalog:' version: 4.0.6(@babel/core@7.28.6)(@glint/template@1.3.0) @@ -1787,7 +1808,7 @@ importers: specifier: ^3.1.1 version: 3.1.1 '@ember/test-helpers': - specifier: ^3.3.1 + specifier: 'catalog:' version: 3.3.1(@babel/core@7.28.6)(@glint/template@1.3.0)(ember-source@5.12.0(patch_hash=2586bd032e74105d65b7953ccaae1cd1d1175047a58ba89ec0057f1f8b7b7fe1)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))(webpack@5.104.1) '@ember/test-waiters': specifier: 'catalog:' @@ -1821,7 +1842,7 @@ importers: version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.12.0(patch_hash=2586bd032e74105d65b7953ccaae1cd1d1175047a58ba89ec0057f1f8b7b7fe1)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))) '@glint/environment-ember-template-imports': specifier: 1.3.0 - version: 1.3.0(@glint/environment-ember-loose@1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.12.0(patch_hash=2586bd032e74105d65b7953ccaae1cd1d1175047a58ba89ec0057f1f8b7b7fe1)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))))(@glint/template@1.3.0) + version: 1.3.0(@glint/environment-ember-loose@1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.12.0(patch_hash=2586bd032e74105d65b7953ccaae1cd1d1175047a58ba89ec0057f1f8b7b7fe1)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))))(@glint/template@1.3.0) '@glint/template': specifier: 1.3.0 version: 1.3.0 @@ -2784,7 +2805,7 @@ importers: version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.12.0(patch_hash=2586bd032e74105d65b7953ccaae1cd1d1175047a58ba89ec0057f1f8b7b7fe1)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))) '@glint/environment-ember-template-imports': specifier: 1.3.0 - version: 1.3.0(@glint/environment-ember-loose@1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.12.0(patch_hash=2586bd032e74105d65b7953ccaae1cd1d1175047a58ba89ec0057f1f8b7b7fe1)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))))(@glint/template@1.3.0) + version: 1.3.0(@glint/environment-ember-loose@1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.12.0(patch_hash=2586bd032e74105d65b7953ccaae1cd1d1175047a58ba89ec0057f1f8b7b7fe1)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))))(@glint/template@1.3.0) '@types/dompurify': specifier: 'catalog:' version: 3.2.0 @@ -2868,7 +2889,7 @@ importers: version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.12.0(patch_hash=2586bd032e74105d65b7953ccaae1cd1d1175047a58ba89ec0057f1f8b7b7fe1)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))) '@glint/environment-ember-template-imports': specifier: ^1.3.0 - version: 1.3.0(@glint/environment-ember-loose@1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.12.0(patch_hash=2586bd032e74105d65b7953ccaae1cd1d1175047a58ba89ec0057f1f8b7b7fe1)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))))(@glint/template@1.3.0) + version: 1.3.0(@glint/environment-ember-loose@1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.12.0(patch_hash=2586bd032e74105d65b7953ccaae1cd1d1175047a58ba89ec0057f1f8b7b7fe1)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))))(@glint/template@1.3.0) '@glint/template': specifier: ^1.3.0 version: 1.3.0 @@ -9572,7 +9593,7 @@ packages: glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me glob@8.1.0: resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} @@ -15883,7 +15904,7 @@ snapshots: ember-cli-htmlbars: 6.3.0 ember-modifier: 4.1.0(ember-source@5.12.0(patch_hash=2586bd032e74105d65b7953ccaae1cd1d1175047a58ba89ec0057f1f8b7b7fe1)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1)) - '@glint/environment-ember-template-imports@1.3.0(@glint/environment-ember-loose@1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.12.0(patch_hash=2586bd032e74105d65b7953ccaae1cd1d1175047a58ba89ec0057f1f8b7b7fe1)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))))(@glint/template@1.3.0)': + '@glint/environment-ember-template-imports@1.3.0(@glint/environment-ember-loose@1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.12.0(patch_hash=2586bd032e74105d65b7953ccaae1cd1d1175047a58ba89ec0057f1f8b7b7fe1)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))))(@glint/template@1.3.0)': dependencies: '@glint/environment-ember-loose': 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.12.0(patch_hash=2586bd032e74105d65b7953ccaae1cd1d1175047a58ba89ec0057f1f8b7b7fe1)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))) '@glint/template': 1.3.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 87e15f278de..dc10b4855c0 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -27,6 +27,7 @@ catalog: "@cardstack/requirejs-monaco-ember-polyfill": ^0.0.1 "@cardstack/view-transitions": ^0.2.0 "@ember/string": ^4.0.1 + "@ember/test-helpers": ^3.3.1 "@ember/test-waiters": ^4.1.1 "@eslint/eslintrc": ^2.1.4 "@eslint/js": ^8.57.1