fix: resolve CI failures by fixing lint config and test setup

- Update .eslintrc.json to ignore inline scripts and type declarations
- Change @ts-ignore to @ts-expect-error in Search.tsx
- Configure vitest aliases to mock scss and inline script imports
- Create proper test mocks and search.test.ts
- Remove template test files (emitter, filter, transformer, helpers)
- Fix formatting in search.scss
This commit is contained in:
saberzero1 2026-02-09 12:58:51 +01:00
parent c997522a5f
commit 28eea49e76
No known key found for this signature in database
11 changed files with 110 additions and 139 deletions

View file

@ -12,7 +12,7 @@
},
"plugins": ["@typescript-eslint"],
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"],
"ignorePatterns": ["dist", "node_modules"],
"ignorePatterns": ["dist", "node_modules", "types/*.d.ts", "src/components/scripts/*.inline.ts"],
"rules": {
"@typescript-eslint/no-unused-vars": [
"error",

View file

@ -6,7 +6,7 @@ import type {
import { classNames } from "../util/lang";
import { i18n } from "../i18n";
import style from "./styles/search.scss";
// @ts-ignore
// @ts-expect-error - inline script imported as string by esbuild loader
import script from "./scripts/search.inline.ts";
export interface SearchOptions {

View file

@ -78,7 +78,9 @@
width: 100%;
border-radius: 7px;
background: var(--light);
box-shadow: 0 14px 50px rgba(27, 33, 48, 0.12), 0 10px 30px rgba(27, 33, 48, 0.16);
box-shadow:
0 14px 50px rgba(27, 33, 48, 0.12),
0 10px 30px rgba(27, 33, 48, 0.16);
margin-bottom: 2em;
}
@ -112,7 +114,12 @@
}
@media all and not (max-width: 800px) {
.search > .search-container > .search-space > .search-layout[data-preview] .result-card > p.preview {
.search
> .search-container
> .search-space
> .search-layout[data-preview]
.result-card
> p.preview {
display: none;
}
@ -201,30 +208,72 @@
color: var(--dark);
}
.search > .search-container > .search-space > .search-layout > .results-container .result-card:hover,
.search > .search-container > .search-space > .search-layout > .results-container .result-card:focus,
.search > .search-container > .search-space > .search-layout > .results-container .result-card.focus {
.search
> .search-container
> .search-space
> .search-layout
> .results-container
.result-card:hover,
.search
> .search-container
> .search-space
> .search-layout
> .results-container
.result-card:focus,
.search
> .search-container
> .search-space
> .search-layout
> .results-container
.result-card.focus {
background: var(--lightgray);
}
.search > .search-container > .search-space > .search-layout > .results-container .result-card > h3 {
.search
> .search-container
> .search-space
> .search-layout
> .results-container
.result-card
> h3 {
margin: 0;
color: var(--secondary);
}
.search > .search-container > .search-space > .search-layout > .results-container .result-card > ul.tags {
.search
> .search-container
> .search-space
> .search-layout
> .results-container
.result-card
> ul.tags {
margin-top: 0.45rem;
margin-bottom: 0;
padding: 0;
list-style: none;
}
.search > .search-container > .search-space > .search-layout > .results-container .result-card > ul.tags > li {
.search
> .search-container
> .search-space
> .search-layout
> .results-container
.result-card
> ul.tags
> li {
display: inline-block;
margin-right: 0.3rem;
}
.search > .search-container > .search-space > .search-layout > .results-container .result-card > ul > li > p {
.search
> .search-container
> .search-space
> .search-layout
> .results-container
.result-card
> ul
> li
> p {
border-radius: 8px;
background-color: var(--highlight);
padding: 0.2rem 0.4rem;
@ -235,7 +284,15 @@
font-size: 0.85rem;
}
.search > .search-container > .search-space > .search-layout > .results-container .result-card > ul > li > p.match-tag {
.search
> .search-container
> .search-space
> .search-layout
> .results-container
.result-card
> ul
> li
> p.match-tag {
color: var(--tertiary);
}
@ -246,6 +303,11 @@
color: var(--gray);
}
.search > .search-container > .search-space > .search-layout > .results-container .result-card.no-match {
.search
> .search-container
> .search-space
> .search-layout
> .results-container
.result-card.no-match {
cursor: default;
}

View file

@ -0,0 +1 @@
export default "";

View file

@ -0,0 +1 @@
export default "";

View file

@ -1,45 +0,0 @@
import { describe, expect, it } from "vitest";
import path from "node:path";
import fs from "node:fs/promises";
import { tmpdir } from "node:os";
import { ExampleEmitter } from "../src/emitter";
import { createCtx, createProcessedContent } from "./helpers";
describe("ExampleEmitter", () => {
it("writes a manifest to the output directory", async () => {
const outputDir = await fs.mkdtemp(path.join(tmpdir(), "quartz-plugin-"));
const ctx = createCtx({ argv: { output: outputDir } });
const emitter = ExampleEmitter({ manifestSlug: "manifest" });
const content = [
createProcessedContent({
slug: "hello-world",
filePath: "notes/hello-world.md",
frontmatter: { title: "Hello", tags: ["docs"] },
}),
];
const result = await emitter.emit(ctx, content, {
css: [],
js: [],
additionalHead: [],
});
const outputPaths = Array.isArray(result) ? result : await collectAsync(result);
const outputPath = outputPaths[0];
if (!outputPath) {
throw new Error("Expected emitter to return an output path");
}
const manifest = JSON.parse(await fs.readFile(outputPath, "utf8"));
expect(outputPath).toContain("manifest.json");
expect(manifest.pages[0].slug).toBe("hello-world");
});
});
const collectAsync = async <T>(iterable: AsyncIterable<T>): Promise<T[]> => {
const results: T[] = [];
for await (const item of iterable) {
results.push(item);
}
return results;
};

View file

@ -1,21 +0,0 @@
import { describe, expect, it } from "vitest";
import { ExampleFilter } from "../src/filter";
import { createCtx, createProcessedContent } from "./helpers";
describe("ExampleFilter", () => {
it("filters drafts by default", () => {
const ctx = createCtx();
const filter = ExampleFilter();
const content = createProcessedContent({ frontmatter: { draft: true } });
expect(filter.shouldPublish(ctx, content)).toBe(false);
});
it("allows drafts when configured", () => {
const ctx = createCtx();
const filter = ExampleFilter({ allowDrafts: true });
const content = createProcessedContent({ frontmatter: { draft: true } });
expect(filter.shouldPublish(ctx, content)).toBe(true);
});
});

View file

@ -1,38 +0,0 @@
import type { BuildCtx } from "@jackyzha0/quartz/util/ctx";
import type { QuartzConfig } from "@jackyzha0/quartz/cfg";
import type { ProcessedContent, QuartzPluginData } from "@jackyzha0/quartz/plugins/vfile";
import { VFile } from "vfile";
type BuildCtxOverrides = Omit<Partial<BuildCtx>, "argv"> & {
argv?: Partial<BuildCtx["argv"]>;
};
export const createCtx = (overrides: BuildCtxOverrides = {}): BuildCtx => {
const { argv: argvOverrides, ...rest } = overrides;
const argv: BuildCtx["argv"] = {
directory: "content",
verbose: false,
output: "dist",
serve: false,
watch: false,
port: 0,
wsPort: 0,
...argvOverrides,
};
return {
buildId: "test-build",
argv,
cfg: {} as QuartzConfig,
allSlugs: [],
allFiles: [],
incremental: false,
...rest,
};
};
export const createProcessedContent = (data: Partial<QuartzPluginData> = {}): ProcessedContent => {
const vfile = new VFile("");
vfile.data = data;
return [{ type: "root", children: [] }, vfile];
};

24
test/search.test.ts Normal file
View file

@ -0,0 +1,24 @@
import { describe, it, expect } from "vitest";
import Search from "../src/components/Search";
describe("Search Component", () => {
it("exports a component factory function", () => {
expect(typeof Search).toBe("function");
});
it("creates a component with default options", () => {
const SearchComponent = Search();
expect(typeof SearchComponent).toBe("function");
});
it("creates a component with custom options", () => {
const SearchComponent = Search({ enablePreview: false });
expect(typeof SearchComponent).toBe("function");
});
it("attaches CSS and script to component", () => {
const SearchComponent = Search();
expect(SearchComponent.css).toBeDefined();
expect(SearchComponent.afterDOMLoaded).toBeDefined();
});
});

View file

@ -1,22 +0,0 @@
import { describe, expect, it } from "vitest";
import { unified } from "unified";
import remarkParse from "remark-parse";
import remarkStringify from "remark-stringify";
import { ExampleTransformer } from "../src/transformer";
import { createCtx } from "./helpers";
describe("ExampleTransformer", () => {
it("highlights text wrapped in the token", async () => {
const ctx = createCtx();
const transformer = ExampleTransformer({ highlightToken: "==" });
const plugins = transformer.markdownPlugins?.(ctx) ?? [];
const file = await unified()
.use(remarkParse)
.use(plugins)
.use(remarkStringify)
.process("Hello ==Quartz==");
expect(String(file)).toContain("**Quartz**");
});
});

View file

@ -1,9 +1,18 @@
import { defineConfig } from "vitest/config";
import path from "path";
export default defineConfig({
test: {
environment: "node",
include: ["test/**/*.test.ts"],
reporters: ["default"],
alias: {
"./styles/search.scss": path.resolve(__dirname, "test/__mocks__/styleMock.ts"),
"./scripts/search.inline.ts": path.resolve(__dirname, "test/__mocks__/scriptMock.ts"),
},
},
esbuild: {
jsx: "automatic",
jsxImportSource: "preact",
},
});