mirror of
https://github.com/Anarios/return-youtube-dislike.git
synced 2026-09-12 10:22:10 +02:00
Feature/add unit testing framework and start writing unit tests (acceptance tests) (#708)
* add jest and rewire for testing * wrap loose code for initMutationObserver() in function and call it instead of just executing it Otherwise this code is executed everytime state.js is imported somewhere in a file. This leads to not being able to test properly, since state.js is imported in a lot of the src files and fails when running the tests since we are missing global environment settings such as document, window etc in the jest environment. * export roundDown and getNumberformatter to make them testable as well * add basic acceptance tests for utils.js, create snapshots and add jest config * add failing test for buggy behaviour in a separate test case and skip it until fixed * Fixes after merge conflicts --------- Co-authored-by: Dmitrii Selivanov <selivano.d@gmail.com>
This commit is contained in:
@@ -32,17 +32,12 @@ api.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
} else if (request.message == "set_state") {
|
||||
// chrome.identity.getAuthToken({ interactive: true }, function (token) {
|
||||
let token = "";
|
||||
fetch(
|
||||
`${apiUrl}/votes?videoId=${request.videoId}&likeCount=${
|
||||
request.likeCount || ""
|
||||
}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
},
|
||||
fetch(`${apiUrl}/votes?videoId=${request.videoId}&likeCount=${request.likeCount || ""}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
},
|
||||
)
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((response) => {
|
||||
sendResponse(response);
|
||||
@@ -147,15 +142,12 @@ async function sendVote(videoId, vote) {
|
||||
async function register() {
|
||||
const userId = generateUserID();
|
||||
api.storage.sync.set({ userId });
|
||||
const registrationResponse = await fetch(
|
||||
`${apiUrl}/puzzle/registration?userId=${userId}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
},
|
||||
const registrationResponse = await fetch(`${apiUrl}/puzzle/registration?userId=${userId}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
},
|
||||
).then((response) => response.json());
|
||||
}).then((response) => response.json());
|
||||
const solvedPuzzle = await solvePuzzle(registrationResponse);
|
||||
if (!solvedPuzzle.solution) {
|
||||
await register();
|
||||
@@ -210,9 +202,7 @@ function countLeadingZeroes(uInt8View, limit) {
|
||||
}
|
||||
|
||||
async function solvePuzzle(puzzle) {
|
||||
let challenge = Uint8Array.from(atob(puzzle.challenge), (c) =>
|
||||
c.charCodeAt(0),
|
||||
);
|
||||
let challenge = Uint8Array.from(atob(puzzle.challenge), (c) => c.charCodeAt(0));
|
||||
let buffer = new ArrayBuffer(20);
|
||||
let uInt8View = new Uint8Array(buffer);
|
||||
let uInt32View = new Uint32Array(buffer);
|
||||
@@ -235,8 +225,7 @@ async function solvePuzzle(puzzle) {
|
||||
}
|
||||
|
||||
function generateUserID(length = 36) {
|
||||
const charset =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
let result = "";
|
||||
if (crypto && crypto.getRandomValues) {
|
||||
const values = new Uint32Array(length);
|
||||
@@ -255,9 +244,7 @@ function generateUserID(length = 36) {
|
||||
|
||||
function storageChangeHandler(changes, area) {
|
||||
if (changes.disableVoteSubmission !== undefined) {
|
||||
handleDisableVoteSubmissionChangeEvent(
|
||||
changes.disableVoteSubmission.newValue,
|
||||
);
|
||||
handleDisableVoteSubmissionChangeEvent(changes.disableVoteSubmission.newValue);
|
||||
}
|
||||
if (changes.coloredThumbs !== undefined) {
|
||||
handleColoredThumbsChangeEvent(changes.coloredThumbs.newValue);
|
||||
@@ -272,21 +259,16 @@ function storageChangeHandler(changes, area) {
|
||||
handleNumberDisplayFormatChangeEvent(changes.numberDisplayFormat.newValue);
|
||||
}
|
||||
if (changes.numberDisplayReformatLikes !== undefined) {
|
||||
handleNumberDisplayReformatLikesChangeEvent(
|
||||
changes.numberDisplayReformatLikes.newValue,
|
||||
);
|
||||
handleNumberDisplayReformatLikesChangeEvent(changes.numberDisplayReformatLikes.newValue);
|
||||
}
|
||||
if (changes.disableLogging !== undefined) {
|
||||
handleDisableLoggingChangeEvent(changes.disableLogging.newValue);
|
||||
}
|
||||
if (changes.showTooltipPercentage !== undefined) {
|
||||
handleShowTooltipPercentageChangeEvent(
|
||||
changes.showTooltipPercentage.newValue,
|
||||
);
|
||||
handleShowTooltipPercentageChangeEvent(changes.showTooltipPercentage.newValue);
|
||||
}
|
||||
if (changes.numberDisplayReformatLikes !== undefined) {
|
||||
handleNumberDisplayReformatLikesChangeEvent(
|
||||
changes.numberDisplayReformatLikes.newValue,
|
||||
);
|
||||
handleNumberDisplayReformatLikesChangeEvent(changes.numberDisplayReformatLikes.newValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,10 +300,8 @@ function handleTooltipPercentageModeChangeEvent(value) {
|
||||
}
|
||||
|
||||
function changeIcon(iconName) {
|
||||
if (api.action !== undefined)
|
||||
api.action.setIcon({ path: "/icons/" + iconName });
|
||||
else if (api.browserAction !== undefined)
|
||||
api.browserAction.setIcon({ path: "/icons/" + iconName });
|
||||
if (api.action !== undefined) api.action.setIcon({ path: "/icons/" + iconName });
|
||||
else if (api.browserAction !== undefined) api.browserAction.setIcon({ path: "/icons/" + iconName });
|
||||
else console.log("changing icon is not supported");
|
||||
}
|
||||
|
||||
@@ -369,12 +349,11 @@ function initializeDisableVoteSubmission() {
|
||||
});
|
||||
}
|
||||
|
||||
function initializeDisableLogging(){
|
||||
api.storage.sync.get(['disableLogging'],(res)=>{
|
||||
function initializeDisableLogging() {
|
||||
api.storage.sync.get(["disableLogging"], (res) => {
|
||||
if (res.disableLogging === undefined) {
|
||||
api.storage.sync.set({disableLogging:true});
|
||||
}
|
||||
else {
|
||||
api.storage.sync.set({ disableLogging: true });
|
||||
} else {
|
||||
extConfig.disableLogging = res.disableLogging;
|
||||
}
|
||||
});
|
||||
@@ -454,7 +433,5 @@ function isChrome() {
|
||||
}
|
||||
|
||||
function isFirefox() {
|
||||
return (
|
||||
typeof browser !== "undefined" && typeof browser.runtime !== "undefined"
|
||||
);
|
||||
return typeof browser !== "undefined" && typeof browser.runtime !== "undefined";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`getNumberFormatter should return a correct formatter when falling back to default locale 1`] = `NumberFormat {}`;
|
||||
|
||||
exports[`getNumberFormatter should return a correct formatter when using chrome 1`] = `NumberFormat {}`;
|
||||
|
||||
exports[`getNumberFormatter should return a correct formatter when using firefox 1`] = `NumberFormat {}`;
|
||||
|
||||
exports[`getNumberFormatter should return a correct formatter when using the URL locale (possibly buggy) 1`] = `NumberFormat {}`;
|
||||
|
||||
exports[`getNumberFormatter should return a correct formatter when using the URL locale 1`] = `NumberFormat {}`;
|
||||
|
||||
exports[`getNumberFormatter should return a correct formatter whith standard option 1`] = `NumberFormat {}`;
|
||||
|
||||
exports[`getNumberFormatter should return a correct formatter with compactLong option 1`] = `NumberFormat {}`;
|
||||
@@ -1,10 +1,4 @@
|
||||
import {
|
||||
getLikeButton,
|
||||
getDislikeButton,
|
||||
getButtons,
|
||||
getLikeTextContainer,
|
||||
getDislikeTextContainer,
|
||||
} from "./buttons";
|
||||
import { getLikeButton, getDislikeButton, getButtons, getLikeTextContainer, getDislikeTextContainer } from "./buttons";
|
||||
import { createRateBar } from "./bar";
|
||||
import {
|
||||
getBrowser,
|
||||
@@ -25,7 +19,7 @@ const NEUTRAL_STATE = "NEUTRAL_STATE";
|
||||
|
||||
let extConfig = {
|
||||
disableVoteSubmission: false,
|
||||
disableLogging: true,
|
||||
disableLogging: false,
|
||||
coloredThumbs: false,
|
||||
coloredBar: false,
|
||||
colorTheme: "classic",
|
||||
@@ -110,11 +104,7 @@ if (isShorts() && !shortsObserver) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
cLog(
|
||||
"Unexpected mutation observer event: " +
|
||||
mutation.target +
|
||||
mutation.type,
|
||||
);
|
||||
cLog("Unexpected mutation observer event: " + mutation.target + mutation.type);
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -123,38 +113,28 @@ if (isShorts() && !shortsObserver) {
|
||||
function isLikesDisabled() {
|
||||
// return true if the like button's text doesn't contain any number
|
||||
if (isMobile()) {
|
||||
return /^\D*$/.test(
|
||||
getButtons().children[0].querySelector(".button-renderer-text").innerText,
|
||||
);
|
||||
return /^\D*$/.test(getButtons().children[0].querySelector(".button-renderer-text").innerText);
|
||||
}
|
||||
return /^\D*$/.test(getLikeTextContainer().innerText);
|
||||
}
|
||||
|
||||
function isVideoLiked() {
|
||||
if (isMobile()) {
|
||||
return (
|
||||
getLikeButton().querySelector("button").getAttribute("aria-label") ===
|
||||
"true"
|
||||
);
|
||||
return getLikeButton().querySelector("button").getAttribute("aria-label") === "true";
|
||||
}
|
||||
return (
|
||||
getLikeButton().classList.contains("style-default-active") ||
|
||||
getLikeButton().querySelector("button")?.getAttribute("aria-pressed") ===
|
||||
"true"
|
||||
getLikeButton().querySelector("button")?.getAttribute("aria-pressed") === "true"
|
||||
);
|
||||
}
|
||||
|
||||
function isVideoDisliked() {
|
||||
if (isMobile()) {
|
||||
return (
|
||||
getDislikeButton().querySelector("button").getAttribute("aria-label") ===
|
||||
"true"
|
||||
);
|
||||
return getDislikeButton().querySelector("button").getAttribute("aria-label") === "true";
|
||||
}
|
||||
return (
|
||||
getDislikeButton().classList.contains("style-default-active") ||
|
||||
getDislikeButton().querySelector("button")?.getAttribute("aria-pressed") ===
|
||||
"true"
|
||||
getDislikeButton().querySelector("button")?.getAttribute("aria-pressed") === "true"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -179,18 +159,14 @@ function setDislikes(dislikesCount) {
|
||||
getDislikeTextContainer()?.removeAttribute("is-empty");
|
||||
if (!isLikesDisabled()) {
|
||||
if (isMobile()) {
|
||||
getButtons().children[1].querySelector(
|
||||
".button-renderer-text",
|
||||
).innerText = dislikesCount;
|
||||
getButtons().children[1].querySelector(".button-renderer-text").innerText = dislikesCount;
|
||||
return;
|
||||
}
|
||||
getDislikeTextContainer().innerText = dislikesCount;
|
||||
} else {
|
||||
cLog("likes count disabled by creator");
|
||||
if (isMobile()) {
|
||||
getButtons().children[1].querySelector(
|
||||
".button-renderer-text",
|
||||
).innerText = localize("TextLikesDisabled");
|
||||
getButtons().children[1].querySelector(".button-renderer-text").innerText = localize("TextLikesDisabled");
|
||||
return;
|
||||
}
|
||||
getDislikeTextContainer().innerText = localize("TextLikesDisabled");
|
||||
@@ -206,8 +182,7 @@ function getLikeCountFromButton() {
|
||||
}
|
||||
|
||||
let likeButton =
|
||||
getLikeButton().querySelector("yt-formatted-string#text") ??
|
||||
getLikeButton().querySelector("button");
|
||||
getLikeButton().querySelector("yt-formatted-string#text") ?? getLikeButton().querySelector("button");
|
||||
|
||||
let likesStr = likeButton.getAttribute("aria-label").replace(/\D/g, "");
|
||||
return likesStr.length > 0 ? parseInt(likesStr) : false;
|
||||
@@ -231,12 +206,8 @@ function processResponse(response, storedData) {
|
||||
if (extConfig.coloredThumbs === true) {
|
||||
if (isShorts()) {
|
||||
// for shorts, leave deactivated buttons in default color
|
||||
let shortLikeButton = getLikeButton().querySelector(
|
||||
"tp-yt-paper-button#button",
|
||||
);
|
||||
let shortDislikeButton = getDislikeButton().querySelector(
|
||||
"tp-yt-paper-button#button",
|
||||
);
|
||||
let shortLikeButton = getLikeButton().querySelector("tp-yt-paper-button#button");
|
||||
let shortDislikeButton = getDislikeButton().querySelector("tp-yt-paper-button#button");
|
||||
if (shortLikeButton.getAttribute("aria-pressed") === "true") {
|
||||
shortLikeButton.style.color = getColorFromTheme(true);
|
||||
}
|
||||
@@ -260,26 +231,19 @@ function displayError(error) {
|
||||
}
|
||||
|
||||
async function setState(storedData) {
|
||||
storedData.previousState = isVideoDisliked()
|
||||
? DISLIKED_STATE
|
||||
: isVideoLiked()
|
||||
? LIKED_STATE
|
||||
: NEUTRAL_STATE;
|
||||
storedData.previousState = isVideoDisliked() ? DISLIKED_STATE : isVideoLiked() ? LIKED_STATE : NEUTRAL_STATE;
|
||||
let statsSet = false;
|
||||
cLog("Video is loaded. Adding buttons...");
|
||||
|
||||
let videoId = getVideoId(window.location.href);
|
||||
let likeCount = getLikeCountFromButton() || null;
|
||||
|
||||
let response = await fetch(
|
||||
`${apiUrl}/votes?videoId=${videoId}&likeCount=${likeCount || ""}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
},
|
||||
let response = await fetch(`${apiUrl}/votes?videoId=${videoId}&likeCount=${likeCount || ""}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
},
|
||||
)
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response.ok) displayError(response.error);
|
||||
return response;
|
||||
|
||||
@@ -66,7 +66,8 @@ function getVideoId(url) {
|
||||
const urlObject = new URL(url);
|
||||
const pathname = urlObject.pathname;
|
||||
if (pathname.startsWith("/clip")) {
|
||||
return (document.querySelector("meta[itemprop='videoId']") || document.querySelector("meta[itemprop='identifier']")).content;
|
||||
return (document.querySelector("meta[itemprop='videoId']") || document.querySelector("meta[itemprop='identifier']"))
|
||||
.content;
|
||||
} else {
|
||||
if (pathname.startsWith("/shorts")) {
|
||||
return pathname.slice(8);
|
||||
@@ -103,7 +104,7 @@ function isVideoLoaded() {
|
||||
}
|
||||
|
||||
function cLog(message, writer) {
|
||||
if (!extConfig.disableLogging){
|
||||
if (!extConfig.disableLogging) {
|
||||
message = `[return youtube dislike]: ${message}`;
|
||||
if (writer) {
|
||||
writer(message);
|
||||
@@ -177,6 +178,7 @@ function createObserver(options, callback) {
|
||||
|
||||
export {
|
||||
numberFormat,
|
||||
getNumberFormatter,
|
||||
getBrowser,
|
||||
getVideoId,
|
||||
isInViewport,
|
||||
|
||||
@@ -0,0 +1,325 @@
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
|
||||
import {
|
||||
__Rewire__ as rewiredUtils,
|
||||
numberFormat,
|
||||
getNumberFormatter,
|
||||
localize,
|
||||
getBrowser,
|
||||
getVideoId,
|
||||
isInViewport,
|
||||
isVideoLoaded,
|
||||
cLog,
|
||||
getColorFromTheme,
|
||||
} from "./utils";
|
||||
import { extConfig } from "./state";
|
||||
|
||||
jest.mock("./state");
|
||||
|
||||
describe("numberFormat", () => {
|
||||
it("should format high numbers correctly", () => {
|
||||
rewiredUtils(
|
||||
"getNumberFormatter",
|
||||
jest.fn().mockReturnValue(
|
||||
Intl.NumberFormat("en", {
|
||||
notation: "compact",
|
||||
compactDisplay: "short",
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
expect(numberFormat(91492)).toBe("91K");
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it("should not format input when < 1000", () => {
|
||||
rewiredUtils(
|
||||
"getNumberFormatter",
|
||||
jest.fn().mockReturnValue(
|
||||
Intl.NumberFormat("en", {
|
||||
notation: "compact",
|
||||
compactDisplay: "short",
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
expect(numberFormat(100)).toBe("100");
|
||||
});
|
||||
|
||||
it("should not round down when rounding is diabled in config", () => {
|
||||
rewiredUtils(
|
||||
"getNumberFormatter",
|
||||
jest.fn().mockReturnValue(
|
||||
Intl.NumberFormat("de", {
|
||||
notation: "compact",
|
||||
compactDisplay: "short",
|
||||
}),
|
||||
),
|
||||
);
|
||||
extConfig.numberDisplayRoundDown = false;
|
||||
|
||||
expect(numberFormat(912391)).toBe("912.391");
|
||||
});
|
||||
});
|
||||
|
||||
describe("getNumberFormatter", () => {
|
||||
it("should return a correct formatter with compactLong option", () => {
|
||||
expect(getNumberFormatter("compactLong")).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should return a correct formatter whith standard option", () => {
|
||||
expect(getNumberFormatter("standard")).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should return a correct formatter when using chrome", () => {
|
||||
Object.defineProperty(document.documentElement, "lang", {
|
||||
value: "en",
|
||||
configurable: true,
|
||||
});
|
||||
expect(getNumberFormatter()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should return a correct formatter when using firefox", () => {
|
||||
Object.defineProperty(document.documentElement, "lang", {
|
||||
value: null,
|
||||
configurable: true,
|
||||
});
|
||||
expect(getNumberFormatter()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should return a correct formatter when using the URL locale (possibly buggy)", () => {
|
||||
// Disclaimer: The case we are testing here is actually not correct and
|
||||
// am almost sure I found a bug in the actual implementation of
|
||||
// getNumberFormatter(). As I am currently writing acceptance tests only
|
||||
// e.g. tests that are exclusively testing the current behaviour as is, as
|
||||
// a first step of adding tests to the codebase, I will add it like this
|
||||
// for now and document how we should actually be testing down below.
|
||||
// The reason why this bug is not causing faulty behaviour is, that we
|
||||
// have a fallback for it, that is always triggered.
|
||||
|
||||
// This is how we make the test go green:
|
||||
Object.defineProperty(document.documentElement, "lang", {
|
||||
value: null,
|
||||
configurable: true,
|
||||
});
|
||||
Object.defineProperty(navigator, "language", {
|
||||
value: null,
|
||||
configurable: true,
|
||||
});
|
||||
const mockedNode = document.createElement("link");
|
||||
|
||||
mockedNode.setAttribute("href", "https://www.youtube.com/opensearch?locale=en");
|
||||
|
||||
document.querySelectorAll = jest.fn().mockReturnValue([mockedNode]);
|
||||
expect(getNumberFormatter()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it.skip("should return a correct formatter when using the URL locale", () => {
|
||||
// But we actually want to test like so, which is the correct value of the locale attribute:
|
||||
Object.defineProperty(document.documentElement, "lang", {
|
||||
value: null,
|
||||
configurable: true,
|
||||
});
|
||||
Object.defineProperty(navigator, "language", {
|
||||
value: null,
|
||||
configurable: true,
|
||||
});
|
||||
const mockedNode = document.createElement("link");
|
||||
mockedNode.setAttribute("href", "https://www.youtube.com/opensearch?locale=en_US");
|
||||
|
||||
document.querySelectorAll = jest.fn().mockReturnValue([mockedNode]);
|
||||
expect(getNumberFormatter()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should return a correct formatter when falling back to default locale", () => {
|
||||
Object.defineProperty(document.documentElement, "lang", {
|
||||
value: null,
|
||||
configurable: true,
|
||||
});
|
||||
Object.defineProperty(navigator, "language", {
|
||||
value: null,
|
||||
configurable: true,
|
||||
});
|
||||
expect(getNumberFormatter()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe("localize", () => {
|
||||
it("should return a translated string", () => {
|
||||
global.chrome = {
|
||||
i18n: {
|
||||
getMessage: () => "Return YouTube Dislike",
|
||||
},
|
||||
};
|
||||
expect(localize("extensionName")).toBe("Return YouTube Dislike");
|
||||
});
|
||||
});
|
||||
|
||||
describe("getBrowser", () => {
|
||||
it("should return a chrome browser instance", () => {
|
||||
global.chrome = {
|
||||
runtime: jest.fn(),
|
||||
};
|
||||
expect(getBrowser()).toBeDefined();
|
||||
});
|
||||
|
||||
it("should return a mozilla browser instance", () => {
|
||||
global.chrome.runtime = undefined;
|
||||
global.browser = {
|
||||
runtime: jest.fn(),
|
||||
};
|
||||
expect(getBrowser()).toBeDefined();
|
||||
});
|
||||
|
||||
it("should fail on unsupported browser", () => {
|
||||
global.chrome = undefined;
|
||||
global.browser = undefined;
|
||||
|
||||
const log = console.log;
|
||||
console.log = jest.fn();
|
||||
|
||||
expect(getBrowser()).toBe(false);
|
||||
expect(console.log).toBeCalledWith("browser is not supported");
|
||||
|
||||
console.log = log;
|
||||
});
|
||||
});
|
||||
describe("getVideoId", () => {
|
||||
it("should return the video Id on valid url input", () => {
|
||||
const url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ";
|
||||
expect(getVideoId(url)).toBe("dQw4w9WgXcQ");
|
||||
});
|
||||
|
||||
it("should return the video Id on valid shorts url input", () => {
|
||||
const url = "https://www.youtube.com/shorts/niMcQ9Vmmj8";
|
||||
expect(getVideoId(url)).toBe("niMcQ9Vmmj8");
|
||||
});
|
||||
|
||||
it("should return the video Id on clip url input", () => {
|
||||
document.querySelector = jest.fn().mockReturnValue({
|
||||
content: "niMcQ9Vmmj8",
|
||||
});
|
||||
const url = "https://www.youtube.com/clip";
|
||||
expect(getVideoId(url)).toBe("niMcQ9Vmmj8");
|
||||
});
|
||||
|
||||
it("should return null on invalid url input", () => {
|
||||
const url = "https://www.youtube.com/wrong-url";
|
||||
expect(getVideoId(url)).toBe(null);
|
||||
});
|
||||
});
|
||||
describe("isInViewport", () => {
|
||||
it("should return true if element is in viewport", () => {
|
||||
const rect = {
|
||||
getBoundingClientRect: jest.fn().mockReturnValue({
|
||||
top: 0,
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
}),
|
||||
};
|
||||
expect(isInViewport(rect)).toBe(false);
|
||||
});
|
||||
|
||||
it("should return false if element is out of viewport", () => {
|
||||
const rect = {
|
||||
getBoundingClientRect: jest.fn().mockReturnValue({
|
||||
top: 0,
|
||||
left: 0,
|
||||
bottom: 769,
|
||||
right: 0,
|
||||
}),
|
||||
};
|
||||
expect(isInViewport(rect)).toBe(false);
|
||||
});
|
||||
});
|
||||
describe("isVideoLoaded", () => {
|
||||
describe("on desktop", () => {
|
||||
it("should return true on loaded video", () => {
|
||||
rewiredUtils("getVideoId", jest.fn().mockReturnValue("fakeId"));
|
||||
document.querySelector = jest.fn().mockReturnValue("notNull");
|
||||
expect(isVideoLoaded()).toBe(true);
|
||||
expect(document.querySelector).toBeCalledWith("ytd-watch-grid[video-id='fakeId']");
|
||||
});
|
||||
|
||||
it("should return false on not loaded video", () => {
|
||||
rewiredUtils("getVideoId", jest.fn().mockReturnValue("fakeId"));
|
||||
document.querySelector = jest.fn().mockReturnValue(null);
|
||||
expect(isVideoLoaded()).toBe(false);
|
||||
expect(document.querySelector).toBeCalledWith("ytd-watch-flexy[video-id='fakeId']");
|
||||
});
|
||||
});
|
||||
|
||||
describe("on mobile", () => {
|
||||
it("should return true on loaded video", () => {
|
||||
rewiredUtils("getVideoId", jest.fn().mockReturnValue("fakeId"));
|
||||
document.querySelector = jest.fn().mockReturnValueOnce(null).mockReturnValue("notNull");
|
||||
expect(isVideoLoaded()).toBe(true);
|
||||
expect(document.querySelector).toHaveBeenLastCalledWith("ytd-watch-flexy[video-id='fakeId']");
|
||||
});
|
||||
|
||||
it("should return false on not loaded video", () => {
|
||||
rewiredUtils("getVideoId", jest.fn().mockReturnValue("fakeId"));
|
||||
document.querySelector = jest.fn().mockReturnValueOnce(null).mockReturnValue(null);
|
||||
expect(isVideoLoaded()).toBe(false);
|
||||
expect(document.querySelector).toHaveBeenLastCalledWith('#player[loading="false"]:not([hidden])');
|
||||
});
|
||||
});
|
||||
});
|
||||
describe("cLog", () => {
|
||||
it("should log a message with the correct prefix", () => {
|
||||
const log = console.log;
|
||||
console.log = jest.fn();
|
||||
cLog("Test message");
|
||||
|
||||
expect(console.log).toBeCalledWith("[return youtube dislike]: Test message");
|
||||
|
||||
console.log = log;
|
||||
});
|
||||
|
||||
it("should log a message with the correct prefix when given a writer", () => {
|
||||
const fakeWriter = jest.fn();
|
||||
|
||||
cLog("Test message", fakeWriter);
|
||||
expect(fakeWriter).toBeCalledWith("[return youtube dislike]: Test message");
|
||||
});
|
||||
});
|
||||
describe("getColorFromTheme", () => {
|
||||
describe("accessible", () => {
|
||||
it("should return the correct color for like", () => {
|
||||
extConfig.colorTheme = "accessible";
|
||||
expect(getColorFromTheme(true)).toBe("dodgerblue");
|
||||
});
|
||||
|
||||
it("should return the correct color for not liked", () => {
|
||||
extConfig.colorTheme = "accessible";
|
||||
expect(getColorFromTheme(false)).toBe("gold");
|
||||
});
|
||||
});
|
||||
|
||||
describe("neon", () => {
|
||||
it("should return the correct color for like", () => {
|
||||
extConfig.colorTheme = "neon";
|
||||
expect(getColorFromTheme(true)).toBe("aqua");
|
||||
});
|
||||
|
||||
it("should return the correct color for not liked", () => {
|
||||
extConfig.colorTheme = "neon";
|
||||
expect(getColorFromTheme(false)).toBe("magenta");
|
||||
});
|
||||
});
|
||||
|
||||
describe("classic", () => {
|
||||
it("should return the correct color for like", () => {
|
||||
extConfig.colorTheme = "classic";
|
||||
expect(getColorFromTheme(true)).toBe("lime");
|
||||
});
|
||||
|
||||
it("should return the correct color for not liked", () => {
|
||||
extConfig.colorTheme = "classic";
|
||||
expect(getColorFromTheme(false)).toBe("red");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
resetMocks: true,
|
||||
collectCoverage: false,
|
||||
collectCoverageFrom: [
|
||||
"Extensions/combined/src/**/*.js",
|
||||
"Extensions/combined/*.js",
|
||||
],
|
||||
};
|
||||
Generated
-8029
File diff suppressed because it is too large
Load Diff
+4
-1
@@ -7,8 +7,8 @@
|
||||
"start": "echo To build for development, please use \"npm run dev\". To build for production, please use \"npm run build\".",
|
||||
"dev": "webpack --mode=production --watch",
|
||||
"build": "webpack --mode=production",
|
||||
"test": "jest",
|
||||
"build:safari": "webpack --mode=production && xcrun safari-web-extension-converter Extensions/combined/dist/safari --project-location Extensions/combined/dist --bundle-identifier com.returnyoutubedislike.safari-ext --force",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"prepare": "husky install"
|
||||
},
|
||||
"lint-staged": {
|
||||
@@ -34,9 +34,12 @@
|
||||
"@babel/preset-env": "^7.23.5",
|
||||
"@babel/runtime": "^7.23.5",
|
||||
"babel-loader": "^9.1.3",
|
||||
"babel-plugin-rewire": "^1.2.0",
|
||||
"copy-webpack-plugin": "^11.0.0",
|
||||
"filemanager-webpack-plugin": "^8.0.0",
|
||||
"husky": "^8.0.0",
|
||||
"jest": "^28.1.3",
|
||||
"jest-environment-jsdom": "^28.1.3",
|
||||
"lint-staged": "^15.2.0",
|
||||
"prettier": "^3.2.2",
|
||||
"webpack": "^5.89.0",
|
||||
|
||||
Reference in New Issue
Block a user