Skip to content

Commit

Permalink
adding new sheet checks to the public repo
Browse files Browse the repository at this point in the history
  • Loading branch information
NBrooks-Roll20 committed Nov 22, 2024
1 parent 52bc9aa commit 9a1fd24
Show file tree
Hide file tree
Showing 42 changed files with 6,796 additions and 0 deletions.
1 change: 1 addition & 0 deletions .actions/sheet-checks/.tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nodejs 20.1.0
8 changes: 8 additions & 0 deletions .actions/sheet-checks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Dependencies

* You will need to install the correct asdf node js listed in the root of this repo's .tools-version file
* Then you will need to run `npm i -g @vercel/ncc`
* Then run `npm run build`

NOTE: You will need to run `npm run build` and commit that change every time you make file changes
in `index.ts`
56 changes: 56 additions & 0 deletions .actions/sheet-checks/__tests__/annotate.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { it, expect, describe, vi } from "vitest"
import { sendAllStatuses } from "../src/annotate";
import { VALIDATION_STATUS } from "../src/statuses";

const hoisted = vi.hoisted(() => {
return {
core: {
error: vi.fn(),
warning: vi.fn(),
notice: vi.fn(),
setFailed: vi.fn(),
},
}
});

vi.mock("@actions/core", () => hoisted.core);

describe("annotate", () => {
describe("sendAllStatuses", () => {
it("should send an error message to the core", () => {
// arrange
const error = VALIDATION_STATUS.CHANGING_MULTIPLE_SHEETS;
const statuses = [error];

// act
sendAllStatuses(statuses);

// assert
expect(hoisted.core.error).toHaveBeenCalledWith(error.description, undefined);
});

it("should send a warning message to the core", () => {
// arrange
const warning = VALIDATION_STATUS.SHEET_HTTP_GET_FAILED;
const statuses = [warning];

// act
sendAllStatuses(statuses);

// assert
expect(hoisted.core.warning).toHaveBeenCalledWith(warning.description, undefined);
});

it("should send a notice message to the core", () => {
// arrange
const notice = VALIDATION_STATUS.CHANGING_DOT_FILE;
const statuses = [notice];

// act
sendAllStatuses(statuses);

// assert
expect(hoisted.core.notice).toHaveBeenCalledWith(notice.description, undefined);
});
});
});
37 changes: 37 additions & 0 deletions .actions/sheet-checks/__tests__/checkAdvanced.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { describe, expect, it } from "vitest";
import { SheetJSON } from "../src/types";
import { isAdvanced } from "../src/checks/checkAdvanced"
import { VALIDATION_STATUS } from "../src/statuses";

const sheetJSON: SheetJSON = {
html: "vampireArchaeologistRPG/varpg.html",
css: "vampireArchaeologistRPG/varpg.html",
preview: "vampireArchaeologistRPG/image.png",
authors: "me",
roll20userid: "01",
advanced: false,
instructions: "You're a vampire and you can't find your stuff! Lie about your credentials so you're allowed to find it."
};

describe("isAdvanced", () => {
it("should return an advancedSheet property of false for empty/false advanced sheetJSON values", () => {
const firstJSON = {...sheetJSON};
delete firstJSON.advanced;
const firstResult = isAdvanced(firstJSON);
expect(firstResult.advancedSheet).toBeFalsy();
const secondResult = isAdvanced(sheetJSON);
expect(secondResult.advancedSheet).toBeFalsy();
})
it("should return an advancedSheet property of true for true advanced sheetJSON values", () => {
const firstJSON = {...sheetJSON};
firstJSON.advanced = true;
const firstResult = isAdvanced(firstJSON);
expect(firstResult.advancedSheet).toBeTruthy();
})
it("should return a status indicating the sheet is being skipped when it finds a true advanced sheetJSON value", () => {
const firstJSON = {...sheetJSON};
firstJSON.advanced = true;
const firstResult = isAdvanced(firstJSON);
expect(firstResult.advancedStatuses).toContainEqual(VALIDATION_STATUS.SKIPPED_ADVANCED_SHEET);
})
})
64 changes: 64 additions & 0 deletions .actions/sheet-checks/__tests__/checkLineEndings.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { describe, it, expect, vi } from "vitest"

import { hasCRLF, checkAllLineEndings } from "../src/checks/checkLineEndings"
import { VALIDATION_STATUS } from "../src/statuses";

const badFile = "This is a bad line\r\nThis is another line";
const goodFile = "This is a bad line\nThis is another line";

const sheetFile = "MidnightTacoBellRPG";

vi.mock("@actions/core", () => {
return {
debug: vi.fn(),
}
});

describe("checkLineEndings", () => {
vi.stubEnv("GITHUB_WORKSPACE", "/github/workspace");

describe("hasCRLF", () => {
it("should return true if it the provided string has CRLF endings", () => {
const result = hasCRLF(badFile)

expect(result).toBeTruthy();
})

it("should return false if the provided string has LF endings", () => {
const result = hasCRLF(goodFile)

expect(result).toBeFalsy();
})
})

describe("checkAllLineEndings", () => {
it("should return a success when all provided file paths have good endings", () => {
const result = checkAllLineEndings({
["a_random_name.html"]: goodFile,
["final_name.html"]: goodFile,
}, sheetFile);
expect(result).toStrictEqual([])
});
it("should return a failure if any provided file paths have bad endings, and include their file paths as errors", () => {
const result = checkAllLineEndings({
[`a_random_name.html`]: goodFile,
[`a_different_name.css`]: badFile,
[`a_third_name.json`]: badFile,
}, sheetFile);
expect(result).toStrictEqual([
{
...VALIDATION_STATUS.INCORRECT_LINE_ENDINGS,
annotation: {
file: `${sheetFile}/a_different_name.css`
}
},
{
...VALIDATION_STATUS.INCORRECT_LINE_ENDINGS,
annotation: {
file: `${sheetFile}/a_third_name.json`,
}
},
])
})
})
})
79 changes: 79 additions & 0 deletions .actions/sheet-checks/__tests__/checkNewSheet.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { it, expect, describe, vi, beforeAll } from "vitest"

import { checkNewSheet } from "../src/checks/checkNewSheet";
import { VALIDATION_STATUS } from "../src/statuses";

const hoisted = vi.hoisted(() => {
return {
fetch: vi.fn(),
core: {
getInput: vi.fn(),
debug: vi.fn(),
},
github: {
getOctokit: vi.fn(),
context: {
payload: {
pull_request: {
number: 123,
},
}
},
},
octokit: {
request: vi.fn(),
},
}
});

vi.stubGlobal("fetch", hoisted.fetch);

vi.mock("@actions/core", () => hoisted.core);

vi.mock("@actions/github", () => hoisted.github);

const emptyResponse = {
json: () => ([]),
}

const responseWithData = {
json: () => ([{
shortname: "bobsburgs",
longname: "Bob's Burgers: The Movie: The Game",
system: "Flipping CORE",
path: "Bobs Burgers the TTRPG",
repo: "roll20-character-sheets",
hidden: false,
official: null,
updated_at: "2024_11_19T11:00:00.000Z",
}]),
}

describe("checkNewSheet", () => {
beforeAll(() => {
hoisted.github.getOctokit.mockReturnValueOnce(hoisted.octokit);
hoisted.octokit.request.mockResolvedValue({ data: [] });
});

describe("checkNewSheet", () => {
const mockSheetName = "Bobs Burgers the TTRPG";

it("returns a success state with a notice if the sheet-http service is not found", async () => {
hoisted.fetch.mockRejectedValueOnce(new Error("this is an error"));
const res = await checkNewSheet(mockSheetName);
expect(res).toStrictEqual([VALIDATION_STATUS.SHEET_HTTP_GET_FAILED])
});

it("returns new sheet true if the sheet-http service is found and the sheet does not exist", async () => {
hoisted.fetch.mockResolvedValueOnce(emptyResponse);
const res = await checkNewSheet(mockSheetName);
expect(res).toStrictEqual([VALIDATION_STATUS.NEW_SHEET])
});

it("returns new sheet false if the sheet-http service is found and the sheet does exist", async () => {
hoisted.fetch.mockResolvedValueOnce(responseWithData);
const res = await checkNewSheet(mockSheetName);
expect(res).toStrictEqual([]);
});
});
});
75 changes: 75 additions & 0 deletions .actions/sheet-checks/__tests__/getFiles.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { beforeEach, afterEach, it, expect, describe, vi } from "vitest"

import { convertToUtf8, getFileList } from "../src/getFiles";

const hoisted = vi.hoisted(() => {
return {
getInput: vi.fn(),
}
});

vi.mock("@actions/core", () => ({
getInput: hoisted.getInput
}));

describe("getFiles", () => {
describe("getFileList", () => {
it("should take a raw file list and a separator and return a list of files", async () => {
// arrange
const rawFileList = "file1,file2,file3";
const sep = ",";
hoisted.getInput.mockReturnValueOnce(rawFileList)
.mockReturnValueOnce(sep);

// act
const rawFiles = getFileList();

// assert
expect(rawFiles).toEqual(["file1", "file2", "file3"]);
});

it("should take a raw file list with subdirectories and a seperator and return a list of files", () => {
// arrange
const rawFileArray = [
"example sheet name/sheet.html",
"example sheet name/sheet.css",
"example sheet name/translation.json",
"example sheet name/sheet.json",
];
const rawFileList = rawFileArray.join(",");
const sep = ",";
hoisted.getInput.mockReturnValueOnce(rawFileList)
.mockReturnValueOnce(sep);

// act
const rawFiles = getFileList();

// assert
expect(rawFiles).toEqual(rawFileArray);
});
});

describe("convertToUtf8", () => {
it("should convert an octal escaped string to utf8", () => {
// arrange
const input = '"Brigandyne 2e \\303\\251dition/sheet.json"';

// act
const output = convertToUtf8(input);

// assert
expect(output).toEqual("Brigandyne 2e édition/sheet.json");
});

it("should convert antoher octal escaped strihng to utf8", () => {
// arrange
const input = '"hell\\303\\264 w\\303\\264rld/sheet.json"';

// act
const output = convertToUtf8(input);

// assert
expect(output).toEqual("hellô wôrld/sheet.json");
});
});
});
Loading

0 comments on commit 9a1fd24

Please sign in to comment.