Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make jest the default test runner #592

Merged
merged 5 commits into from
Apr 13, 2022
Merged
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
1 change: 0 additions & 1 deletion .github/workflows/test-server.yml
Original file line number Diff line number Diff line change
@@ -24,7 +24,6 @@ jobs:
npm run build
pip3 install setuptools==33.1.1
pip3 install -r genanki/requirements.txt
npm install -g ava
npm run test
env:
CI: true
215 changes: 215 additions & 0 deletions server/lib/notion/BlockHandler.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
import path from "path";
import os from "os";

import CustomExporter from "../parser/CustomExporter";
import Note from "../parser/Note";
import ParserRules from "../parser/ParserRules";

import Settings from "../parser/Settings";
import Workspace from "../parser/WorkSpace";
import BlockHandler from "./BlockHandler";
import { pageId as examplId } from "../../test/test-utils";
import MockNotionAPI from "./_mock/MockNotionAPI";

import * as dotenv from "dotenv";
dotenv.config({ path: "test/.env" });
const api = new MockNotionAPI(process.env.NOTION_KEY!);

const loadCards = async (
options: any,
pageId: string,
ws: Workspace,
rules?: ParserRules
): Promise<Note[]> => {
const settings = new Settings(options);
const r = rules || new ParserRules();
const exporter = new CustomExporter("", ws.location);
const bl = new BlockHandler(exporter, api);
const decks = await bl.findFlashcards(pageId, r, settings, []);
return decks[0].cards;
};

const defaultFront = (s: string) => `<div class="">${s}</div>`;

async function findCardByName(
name: string,
options: Object
): Promise<Note | undefined> {
const flashcards = await loadCards(
options,
examplId,
new Workspace(true, "fs"),
new ParserRules()
);
return flashcards.find((f) => f.name === defaultFront(name));
}

beforeEach(() => {
process.env.WORKSPACE_BASE = path.join(os.tmpdir(), "workspaces");
});

test("Get Notion Page", async () => {
const page = await api.getPage("3ce6b147ac8a425f836b51cc21825b85");
const title = await api.getPageTitle(page, new Settings({}));
expect(title).toBe("Notion API Test Page");
});

test("Get Blocks", async () => {
// This should be mocked
const blocks = await api.getBlocks("07a7b319183642b9afecdcc4c456f73d", true);
/* @ts-ignore */
const topLevelToggles = blocks.results.filter((t) => t.type === "toggle");
expect(topLevelToggles.length).toEqual(14);
});

test.skip("Toggle Headings in HTML export", async () => {
const r = new ParserRules();
r.setFlashcardTypes(["heading"]);
const cards = await loadCards(
{},
"25226df63b4d4895a71f3bba01d8a8f3",
new Workspace(true, "fs"),
r
);
console.log("cards", JSON.stringify(cards, null, 4));
expect(cards.length).toBe(1);
});

test.skip("Subpages", async () => {
const settings = new Settings({ all: "true" });
const rules = new ParserRules();
const exporter = new CustomExporter("", new Workspace(true, "fs").location);
const bl = new BlockHandler(exporter, api);
const decks = await bl.findFlashcards(examplId, rules, settings, []);

expect(decks.length > 1).toBe(true);
expect(decks[1].name.includes("::")).toBe(true);
});

test.skip("Toggle Mode", async () => {
const flashcards = await loadCards(
{},
examplId,
new Workspace(true, "fs"),
new ParserRules()
);
const nestedOnes = flashcards.find((c) => c.name.match(/Nested/i));
expect(nestedOnes?.back).toBe(true);
});

test.skip("Strikethrough Local Tags", async () => {
const card = await findCardByName("This card has three tags", {
tags: "true",
});
const expected = ["global tag", "tag a", "tag b"];
expect(card?.tags).toBe(expected);
});

test("Basic Cards from Blocks", async () => {
const flashcards = await loadCards(
{ cloze: "false" },
examplId,
new Workspace(true, "fs"),
new ParserRules()
);
const card = flashcards[0];
expect(card.name).toBe(defaultFront("1 - This is a basic card"));
expect(card.back).toBe(
`<p class="" id="f83ce56a-9039-4888-81be-375b19a84790">This is the back of the card</p>`
);
});

test("Cloze Deletion from Blocks", async () => {
const flashcards = await loadCards(
{ cloze: true },
examplId,
new Workspace(true, "fs"),
new ParserRules()
);
const card = flashcards[1];
expect(card.name).toBe("2 - This is a {{c1::cloze deletion}}");
expect(card.back).toBe(
`<p class="" id="34be35bd-db68-4588-85d9-e1adc84c45a5">Extra</p>`
);
});

test("Input Cards from Blocks", async () => {
const flashcards = await loadCards(
{ cloze: "false", input: "true" },
examplId,
new Workspace(true, "fs"),
new ParserRules()
);
expect(flashcards.find((n) => n.name == "6 - 21 + 21 is ")).toBeTruthy();
});

test("Enable Cherry Picking Using 🍒 Emoji", async () => {
const flashcards = await loadCards(
{ cherry: "true", cloze: "true" },
examplId,
new Workspace(true, "fs")
);
expect(flashcards.length).toBe(2);
});

test("Only Create Flashcards From Toggles That Don't Have The 🥑 Emoji", async () => {
const flashcards = await loadCards(
{ avocado: "true" },
examplId,
new Workspace(true, "fs"),
new ParserRules()
);
const avocado = flashcards.find((c) => c.name.includes("🥑"));
expect(avocado).toBeFalsy();
});

test("Add Notion Link", async () => {
const expected =
"https://www.notion.so/Notion-API-Test-Page-3ce6b147ac8a425f836b51cc21825b85#e5201f35c72240d38e3a5d218e5d80a5";
const flashcards = await loadCards(
{
"add-notion-link": true,
parentBlockId: examplId,
},
examplId,
new Workspace(true, "fs"),
new ParserRules()
);
const card = flashcards.find(
(f) => f.name === defaultFront("1 - This is a basic card")
);
expect(card).toBeTruthy();
expect(card?.notionLink).toBe(expected);
});

test("Use Notion ID", async () => {
const flashcards = await loadCards(
{ "use-notion-id": "true" },
examplId,
new Workspace(true, "fs"),
new ParserRules()
);
const card = flashcards.find(
(f) => f.name === defaultFront("3 - 21 + 21 is #buddy")
);
const expected = "a5445230-bfa9-4bf1-bc35-a706c1d129d1";
expect(card?.notionId).toBe(expected);
});

test("Strikethrough Global Tags", async () => {
const card = await findCardByName("This card has global tags", {
tags: "true",
});
expect(card?.tags.includes("global-tag")).toBe(true);
expect(card?.tags.includes("global-tag")).toBe(true);
});

test.todo("Maximum One Toggle Per Card");
test.todo("Use All Toggle Lists");
test.todo("Template Options");
test.todo("Use Plain Text for Back");
test.todo("Basic and Reversed");
test.todo("Just the Reversed Flashcards");
test.todo("Remove Underlines");
test.todo("Download Media Files");
test.todo("Preserve Newlines in the Toggle Header and Body");
6 changes: 3 additions & 3 deletions server/lib/notion/NotionAPIWrapper.ts
Original file line number Diff line number Diff line change
@@ -169,13 +169,13 @@ class NotionAPIWrapper {
}

async getPageTitle(
page: GetPageResponse,
page: GetPageResponse | null,
settings: Settings
): Promise<string> {
console.debug(`getPageTitle: ${JSON.stringify(page.id, null, 4)}`);
if (!page) {
throw new Error("missing page");
return "";
}
console.debug(`getPageTitle: ${JSON.stringify(page.id, null, 4)}`);

let title = "Untitled: " + new Date();
let icon = "";
File renamed without changes.
60 changes: 0 additions & 60 deletions server/lib/notion/_BlockHandler.test.ts

This file was deleted.

60 changes: 60 additions & 0 deletions server/lib/notion/_mock/MockNotionAPI.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import {
GetBlockResponse,
GetPageResponse,
ListBlockChildrenResponse,
QueryDatabaseResponse,
} from "@notionhq/client/build/src/api-endpoints";
import NotionAPIWrapper from "../NotionAPIWrapper";
import dataMockPath from "./helpers/dataMockPath";
import { mockDataExists } from "./helpers/mockDataExists";
import getPayload from "./helpers/getPayload";
import savePayload from "./helpers/savePayload";

export default class MockNotionAPI extends NotionAPIWrapper {
async getBlocks(
id: string,
all?: boolean
): Promise<ListBlockChildrenResponse> {
if (mockDataExists("ListBlockChildrenResponse", id)) {
console.info("block exists using mock", id);
return getPayload(dataMockPath("ListBlockChildrenResponse", id));
}
const blocks = await super.getBlocks(id, all);
savePayload(dataMockPath("ListBlockChildrenResponse", id), blocks);
return blocks;
}

async getPage(id: string): Promise<GetPageResponse | null> {
if (mockDataExists("GetPageResponse", id)) {
console.info("page exists using mock", id);
return getPayload(dataMockPath("GetPageResponse", id));
}
const page = await super.getPage(id);
savePayload(dataMockPath("GetPageResponse", id), page);
return page;
}

async getBlock(id: string): Promise<GetBlockResponse> {
if (mockDataExists("GetBlockResponse", id)) {
console.info("block exists using mock", id);
return getPayload(dataMockPath("GetBlockResponse", id));
}
const block = await super.getBlock(id);
savePayload(dataMockPath("GetBlockResponse", id), block);
return block;
}

async queryDatabase(
id: string,
all?: boolean
): Promise<QueryDatabaseResponse> {
if (mockDataExists("QueryDatabaseResponse", id)) {
console.info("database exists using mock", id);
return getPayload(dataMockPath("QueryDatabaseResponse", id));
}
const query = await super.queryDatabase(id, all);
savePayload(dataMockPath("QueryDatabaseResponse", id), query);
return query;
}
// do we need to mock search?
}
6 changes: 6 additions & 0 deletions server/lib/notion/_mock/helpers/MockType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export type MockType =
| "ListBlockChildrenResponse"
| "GetPageResponse"
| "GetDatabaseResponse"
| "QueryDatabaseResponse"
| "GetBlockResponse";
9 changes: 9 additions & 0 deletions server/lib/notion/_mock/helpers/dataMockPath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import path from "path";
import ensureExists from "./ensureExists";
import { MockType } from "./MockType";

export default function dataMockPath(type: MockType, id: string): string {
const dir = path.join(__dirname, `../payloads/${type}`);
ensureExists(dir);
return path.join(dir, `${id}.json`);
}
8 changes: 8 additions & 0 deletions server/lib/notion/_mock/helpers/ensureExists.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import fs from "fs";

export default function ensureExists(location: string) {
if (!fs.existsSync(location)) {
console.info("creating: " + location);
fs.mkdirSync(location, { recursive: true });
}
}
5 changes: 5 additions & 0 deletions server/lib/notion/_mock/helpers/getPayload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import fs from "fs";

export default function getPayload(path: string): any {
return JSON.parse(fs.readFileSync(path).toString());
}
Loading