Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ module(`Integration | prerendered-card-search`, function (hooks) {
'person.gts': { PersonField },
'post.gts': { Post },
'publisher.gts': { Publisher },
'sample-doc.md': '# Test Document\n\nSome content here.',
...sampleCards,
},
}));
Expand Down Expand Up @@ -836,4 +837,48 @@ module(`Integration | prerendered-card-search`, function (hooks) {
.dom('[data-test-meta-page-total="5"]')
.exists('meta.page.total remains correct on last page');
});

test(`can search for files using a FileDef type filter`, async function (assert) {
let query: Query = {
filter: {
type: {
module: `${baseRealm.url}markdown-file-def`,
name: 'MarkdownDef',
},
},
};
let realms = [testRealmURL];

await render(
<template>
<PrerenderedCardSearch
@query={{query}}
@format='embedded'
@realms={{realms}}
>
<:loading>
Loading...
</:loading>
<:response as |cards|>
{{#each cards as |card|}}
<card.component />
{{/each}}
</:response>
<:meta as |meta|>
<div data-test-meta-page-total={{meta.page.total}}></div>
</:meta>
</PrerenderedCardSearch>
</template>,
);
await waitFor('#ember-testing > [data-test-boxel-card-container]');
assert
.dom('#ember-testing > [data-test-boxel-card-container]')
.exists({ count: 1 }, 'one markdown file result is rendered');
assert
.dom('#ember-testing > [data-test-boxel-card-container]')
.containsText('Test Document', 'markdown file title appears in rendered html');
assert
.dom('[data-test-meta-page-total="1"]')
.exists('meta.page.total is correct for file-meta search');
});
});
194 changes: 194 additions & 0 deletions packages/realm-server/tests/search-prerendered-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -973,6 +973,200 @@ module(basename(__filename), function () {
});
});

module('file-meta queries', function (hooks) {
setupPermissionedRealm(hooks, {
realmURL,
permissions: {
'*': ['read'],
},
fileSystem: {
'sample.md': `# Test Document\n\nThis is a sample markdown file for testing.`,
'notes.md': `# Notes\n\nSome notes content here.`,
'readme.txt': 'Plain text file contents.',
'person.gts': `
import { contains, field, CardDef, Component } from "https://cardstack.com/base/card-api";
import StringField from "https://cardstack.com/base/string";

export class Person extends CardDef {
@field firstName = contains(StringField);
static embedded = class Embedded extends Component<typeof this> {
<template>
Embedded Card Person: <@fields.firstName/>
</template>
}
}
`,
'john.json': {
data: {
attributes: {
firstName: 'John',
},
meta: {
adoptsFrom: {
module: './person',
name: 'Person',
},
},
},
},
},
onRealmSetup,
});

test('returns prerendered file results for FileDef type filter', async function (assert) {
let query: Query & { prerenderedHtmlFormat: string } = {
filter: {
type: {
module: `https://cardstack.com/base/file-api`,
name: 'FileDef',
},
},
prerenderedHtmlFormat: 'embedded',
};
let response = await request
.post(searchPath)
.set('Accept', 'application/vnd.card+json')
.set('X-HTTP-Method-Override', 'QUERY')
.send(query);

assert.strictEqual(response.status, 200, 'HTTP 200 status');
let json = response.body;

assert.true(
json.data.length >= 2,
'at least the markdown files are returned',
);

json.data.forEach(
(item: { type: string; attributes: { html: string } }) => {
assert.strictEqual(
item.type,
'prerendered-card',
'result type is prerendered-card',
);
assert.ok(
item.attributes.html,
'result has non-empty html attribute',
);
},
);

assert.true(
json.meta.page.total >= 2,
'total count includes file results',
);
});

test('returns prerendered file results for MarkdownDef subclass filter', async function (assert) {
let query: Query & { prerenderedHtmlFormat: string } = {
filter: {
type: {
module: `https://cardstack.com/base/markdown-file-def`,
name: 'MarkdownDef',
},
},
prerenderedHtmlFormat: 'embedded',
};
let response = await request
.post(searchPath)
.set('Accept', 'application/vnd.card+json')
.set('X-HTTP-Method-Override', 'QUERY')
.send(query);

assert.strictEqual(response.status, 200, 'HTTP 200 status');
let json = response.body;

assert.strictEqual(
json.data.length,
2,
'only markdown files are returned',
);

// MarkdownDef uses MarkdownFilePreview template which renders
// an article with class 'markdown-file-preview'
let htmls = json.data.map((item: { attributes: { html: string } }) =>
item.attributes.html.replace(/\s+/g, ' '),
);
assert.true(
htmls.some((html: string) => html.includes('Test Document')),
'sample.md title appears in prerendered html',
);
assert.true(
htmls.some((html: string) => html.includes('Notes')),
'notes.md title appears in prerendered html',
);

assert.strictEqual(
json.meta.page.total,
2,
'total count matches markdown files',
);
});

test('returns fitted format for file results', async function (assert) {
let query: Query & { prerenderedHtmlFormat: string } = {
filter: {
type: {
module: `https://cardstack.com/base/markdown-file-def`,
name: 'MarkdownDef',
},
},
prerenderedHtmlFormat: 'fitted',
};
let response = await request
.post(searchPath)
.set('Accept', 'application/vnd.card+json')
.set('X-HTTP-Method-Override', 'QUERY')
.send(query);

assert.strictEqual(response.status, 200, 'HTTP 200 status');
let json = response.body;

assert.strictEqual(
json.data.length,
2,
'markdown files are returned',
);

json.data.forEach(
(item: { type: string; attributes: { html: string } }) => {
assert.strictEqual(
item.type,
'prerendered-card',
'result type is prerendered-card',
);
assert.ok(item.attributes.html, 'fitted html is non-empty');
},
);
});

test('file-meta query does not return card instances', async function (assert) {
let query: Query & { prerenderedHtmlFormat: string } = {
filter: {
type: {
module: `https://cardstack.com/base/markdown-file-def`,
name: 'MarkdownDef',
},
},
prerenderedHtmlFormat: 'embedded',
};
let response = await request
.post(searchPath)
.set('Accept', 'application/vnd.card+json')
.set('X-HTTP-Method-Override', 'QUERY')
.send(query);

let json = response.body;

// Verify no card instances (john.json) are in the results
let ids = json.data.map((item: { id: string }) => item.id);
assert.false(
ids.some((id: string) => id.includes('john.json')),
'card instance john.json is not in file-meta results',
);
});
});

module('permissioned realm', function (hooks) {
setupPermissionedRealm(hooks, {
realmURL,
Expand Down
3 changes: 2 additions & 1 deletion packages/runtime-common/index-query-engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,7 @@ export class IndexQueryEngine {
realmURL: URL,
{ filter, sort, page }: Query,
opts: QueryOptions = { includeErrors: true },
entryType: 'instance' | 'file' = 'instance',
): Promise<{
prerenderedCards: PrerenderedCard[];
scopedCssUrls: string[];
Expand Down Expand Up @@ -617,7 +618,7 @@ export class IndexQueryEngine {
' as used_render_type,',
'ANY_VALUE(deps) as deps',
],
'instance',
entryType,
)) as {
meta: QueryResultsMeta;
results: (Partial<BoxelIndexTable> & {
Expand Down
2 changes: 2 additions & 0 deletions packages/runtime-common/realm-index-query-engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,10 +277,12 @@ export class RealmIndexQueryEngine {
}

async searchPrerendered(query: Query, opts?: Options) {
let isFileMetaQuery = await this.queryTargetsFileMeta(query.filter, opts);
let results = await this.#indexQueryEngine.searchPrerendered(
new URL(this.#realm.url),
query,
opts,
isFileMetaQuery ? 'file' : 'instance',
);

return results;
Expand Down
Loading