Skip to content

Get types to work inside of a realm test file locally#4181

Open
tintinthong wants to merge 9 commits intomainfrom
getting-test-to-work-for-test-file
Open

Get types to work inside of a realm test file locally#4181
tintinthong wants to merge 9 commits intomainfrom
getting-test-to-work-for-test-file

Conversation

@tintinthong
Copy link
Contributor

@tintinthong tintinthong commented Mar 13, 2026

Look at sample-command-card

@tintinthong tintinthong force-pushed the getting-test-to-work-for-test-file branch from 9832184 to c2aff15 Compare March 13, 2026 02:34
@github-actions
Copy link

github-actions bot commented Mar 13, 2026

Host Test Results

    1 files  ±0      1 suites  ±0   2h 31m 47s ⏱️ +43s
2 025 tests ±0  2 010 ✅ ±0  15 💤 ±0  0 ❌ ±0 
2 040 runs  ±0  2 025 ✅ ±0  15 💤 ±0  0 ❌ ±0 

Results for commit dad6552. ± Comparison against base commit 9166312.

♻️ This comment has been updated with latest results.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 98321845b0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +50 to +51
<script src="/assets/test-support.js"></script>
<script src="/assets/@cardstack/host.js"></script>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Disable QUnit autostart before loading test-support bundle

live-test.html currently loads /assets/test-support.js before /assets/live-test.js, but test-support executes tests/test-helper.js, which unconditionally calls start() and starts QUnit immediately. That makes QUnit.config.autostart = false in live-test.js too late, so the run can start/finish before the realm module is imported (resulting in zero/missing realm tests or mixed host-suite execution). Move the live-test bootstrap (or an early autostart=false script) ahead of test-support.js.

Useful? React with 👍 / 👎.

@github-actions
Copy link

Preview deployments

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just as a note, these types can end up diverging from the actual implementations in host. consider using an interface/type that that lives in runtime common or some other exportable package that both these types and the ones in host implement so that we can keep prevent diverging types

Copy link
Contributor Author

@tintinthong tintinthong Mar 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually fell back to treating @cardstack/host/test/helpers as paths resolved similar to @cardsatck/boxel-host/commands inside tsconfig

That has simplified my solution and just allowed me to access types locally. In the following PR, I want to implement a setup that doesn't expose the complexity of setting up an integration test to the user. But this PR also covers basics like enabling qunit to be available in typeland for the catalog and experiments realm

We will handle lint of dynamically edited files as a code mode problem in the future

@tintinthong tintinthong force-pushed the getting-test-to-work-for-test-file branch from 6798b2b to a834eac Compare March 17, 2026 03:08
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aims to make TypeScript types resolve correctly when writing “realm-local” test code (as shown in sample-command-card.gts), primarily by centralizing needed test deps in the pnpm catalog and updating realm package tsconfigs/deps to reference host test helpers.

Changes:

  • Added @ember/test-helpers to the pnpm catalog and switched packages/host to consume it via catalog:.
  • Updated catalog-realm and experiments-realm tsconfigs to include **/*.gts and added a path alias for @cardstack/host/tests/*.
  • Added a new sample-command-card.gts in experiments-realm that includes both a card and a QUnit test using host test helpers.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
pnpm-workspace.yaml Adds @ember/test-helpers to the centralized pnpm catalog (and normalizes quoting).
pnpm-lock.yaml Lockfile updates reflecting new catalog entry and added realm devDependencies.
packages/host/package.json Switches @ember/test-helpers dependency to catalog: for version centralization.
packages/experiments-realm/tsconfig.json Adds @cardstack/host/tests/* path mapping and includes **/*.gts for typechecking.
packages/experiments-realm/sample-command-card.gts Introduces a sample card plus embedded QUnit test code that imports host test helpers.
packages/experiments-realm/package.json Adds test-related devDependencies needed by the sample test code.
packages/catalog-realm/tsconfig.json Adds @cardstack/host/tests/* path mapping and includes **/*.gts for typechecking.
packages/catalog-realm/package.json Adds test-related devDependencies (presumably for future realm-local test typing).
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +15 to +105

// ── 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, type Store } from '@cardstack/runtime-common';
import {
setupIntegrationTestRealm,
setupLocalIndexing,
setupOnSave,
setupCardLogs,
testRealmURL,
setupRealmCacheTeardown,
withCachedRealmSetup,
type TestContextWithSave,
} from '@cardstack/host/tests/helpers';
import { TestRealmAdapter } from '@cardstack/host/tests/helpers/adapter';
import { setupMockMatrix } from '@cardstack/host/tests/helpers/mock-matrix';
import { setupRenderingTest } from '@cardstack/host/tests/helpers/setup';

class CreateCardButton extends GlimmerComponent {
@service declare store: Store;

createCard = async () => {
await this.store.add(new SampleCommandCard({ title: 'Hello from live-test' }));
};

<template>
<button type='button' {{on 'click' this.createCard}}>Create Card</button>
</template>
}

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(<template><CreateCardButton /></template>);
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');
},
);
});
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Im perfroming a full reindex on experiments to be certain this doesn't break the indexer. Which is the key issue

Copy link
Contributor Author

@tintinthong tintinthong Mar 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to have succesfully indexed the file independent of the host

Copy link
Contributor

@habdelra habdelra Mar 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a really important comment: specifically, due to the static nature you have defined the modules tests are run as a result of just loading this card. I don't think we ever want that. that means indexing is actually running tests (other things will end up running tests--like the user just interacting with this card will always end up running tests as a result of using this card). we should never have the module() or test() functions in module scope. A better shape might be:

export function runTests() {
  module('my test module', function (hooks) {
    test('test 1', function (assert) {});
   test('test 2', function (assert) {});
  });
}

and then tests are only executed when runTests() is called, not as a side effect of just loading the module.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i feel so strongly about this, that we should probably include a lint and/or skills for this as well, because users or AI might author cards in this way. this would be a very bad performance hit to our indexing system if this gets into a card.

Comment on lines +84 to +103
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(<template><CreateCardButton /></template>);
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');
},
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants