Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
104 changes: 104 additions & 0 deletions scripts/notion-translate/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1392,6 +1392,110 @@ describe("notion-translate index", () => {
});
});

describe("toggle _category_.json fallback behavior", () => {
const mockConfig = {
language: "pt-BR",
notionLangCode: "Portuguese",
outputDir: "/test/output",
};

it("falls back to English title when translatedTitle is empty string", async () => {
const { saveTranslatedContentToDisk } = await import("./index");

const togglePage = createMockNotionPage({
id: "toggle-abc123",
title: "English Toggle Title",
elementType: "Toggle",
});

await saveTranslatedContentToDisk(togglePage, "", "", mockConfig);

const writeCall = mockWriteFile.mock.calls.find((call: string[]) =>
call[0].endsWith("_category_.json")
);
expect(writeCall).toBeDefined();
const written = JSON.parse(writeCall[1] as string);
expect(written.label).toBe("English Toggle Title");
expect(written.customProps.title).toBe("English Toggle Title");
});

it("falls back to English title when translatedTitle is whitespace-only", async () => {
const { saveTranslatedContentToDisk } = await import("./index");

const togglePage = createMockNotionPage({
id: "toggle-def456",
title: "English Toggle Title",
elementType: "Toggle",
});

await saveTranslatedContentToDisk(togglePage, "", " ", mockConfig);

const writeCall = mockWriteFile.mock.calls.find((call: string[]) =>
call[0].endsWith("_category_.json")
);
expect(writeCall).toBeDefined();
const written = JSON.parse(writeCall[1] as string);
expect(written.label).toBe("English Toggle Title");
expect(written.customProps.title).toBe("English Toggle Title");
});

it("uses translatedTitle when it is non-empty", async () => {
const { saveTranslatedContentToDisk } = await import("./index");

const togglePage = createMockNotionPage({
id: "toggle-ghi789",
title: "English Toggle Title",
elementType: "Toggle",
});

await saveTranslatedContentToDisk(
togglePage,
"",
"Título Traduzido",
mockConfig
);

const writeCall = mockWriteFile.mock.calls.find((call: string[]) =>
call[0].endsWith("_category_.json")
);
expect(writeCall).toBeDefined();
const written = JSON.parse(writeCall[1] as string);
expect(written.label).toBe("Título Traduzido");
expect(written.customProps.title).toBe("Título Traduzido");
});
});

describe("non-toggle page output regression", () => {
it("writes .mdx file and does not produce _category_.json for page-type content", async () => {
const { saveTranslatedContentToDisk } = await import("./index");

const regularPage = createMockNotionPage({
id: "page-regular123",
title: "Regular Page",
elementType: "Page",
});

const filePath = await saveTranslatedContentToDisk(
regularPage,
"# Translated Content",
"Translated Title",
{
language: "pt-BR",
notionLangCode: "Portuguese",
outputDir: "/test/output",
}
);

expect(filePath).toMatch(/\.md$/);
expect(filePath).not.toContain("_category_.json");

const categoryCall = mockWriteFile.mock.calls.find((call: string[]) =>
call[0].endsWith("_category_.json")
);
expect(categoryCall).toBeUndefined();
});
});

describe("missing parent relation handling", () => {
it("gracefully skips pages without Parent item relation and reports as non-critical failure", async () => {
// Create a page WITHOUT parent relation
Expand Down
5 changes: 3 additions & 2 deletions scripts/notion-translate/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -681,8 +681,9 @@ export async function saveTranslatedContentToDisk(
await fs.mkdir(sectionPath, { recursive: true });

// Create _category_.json file
const effectiveTitle = translatedTitle?.trim() || title || "Untitled";
const categoryContent = {
label: translatedTitle,
label: effectiveTitle,
position:
(
englishPage.properties[NOTION_PROPERTIES.ORDER] as
Expand All @@ -695,7 +696,7 @@ export async function saveTranslatedContentToDisk(
type: "generated-index",
},
customProps: {
title: translatedTitle,
title: effectiveTitle,
},
};

Expand Down
65 changes: 65 additions & 0 deletions scripts/verify-locale-output.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,71 @@ describe("Locale Output Verification", () => {
).toBeLessThanOrEqual(maxAllowedDiff);
});

it("has non-empty label in translated toggle _category_.json files", async () => {
const locales = ["es", "pt"];
for (const locale of locales) {
const localeDocsDir = path.join(
i18nDir,
locale,
"docusaurus-plugin-content-docs",
"current"
);
let categoryFiles: string[];
try {
// Recursively find all _category_.json files under the locale docs dir
const findCategoryFiles = async (dir: string): Promise<string[]> => {
const results: string[] = [];
let entries;
try {
entries = await fs.readdir(dir, { withFileTypes: true });
} catch {
return results;
}
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
results.push(...(await findCategoryFiles(fullPath)));
} else if (entry.name === "_category_.json") {
results.push(fullPath);
}
}
return results;
};
categoryFiles = await findCategoryFiles(localeDocsDir);
} catch (error) {
if (
error instanceof Error &&
"code" in error &&
(error as NodeJS.ErrnoException).code === "ENOENT"
) {
console.log(
`${locale} locale docs directory not found - content branch may not have toggle pages`
);
continue;
}
throw error;
}

if (categoryFiles.length === 0) {
console.log(
`No _category_.json files found for locale ${locale} - may not have toggle pages`
);
continue;
}

for (const filePath of categoryFiles) {
const content = await fs.readFile(filePath, "utf8");
const category = JSON.parse(content);
expect(
category.label,
`_category_.json at ${filePath} has empty label`
).toBeTruthy();
expect(typeof category.label).toBe("string");
expect(category.label.trim().length).toBeGreaterThan(0);
}
}
});

it("does not have English locale directory (en/)", async () => {
const enDir = path.join(i18nDir, "en");

Expand Down
Loading