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

test: cover part of helpers #92

Merged
merged 10 commits into from
Oct 23, 2023
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ dist/
.DS_Store
.env*
!.env.example
coverage/

# Temporary files
cache*.json
Expand Down
3 changes: 3 additions & 0 deletions meta/testing/jest.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ module.exports = {
transform: {
"^.+\\.ts?$": ["ts-jest", {
useESM: true,
}],
"^.+\\.js?$": ["ts-jest", {
useESM: true,
}]
},
verbose: false,
Expand Down
Binary file added src/helpers/medias/__tests__/files/image-gif.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/helpers/medias/__tests__/files/image-jpeg.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/helpers/medias/__tests__/files/image-jpg.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/helpers/medias/__tests__/files/image-png.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/helpers/medias/__tests__/files/image-webp.webp
Binary file not shown.
Binary file added src/helpers/medias/__tests__/files/video-mp4.mp4
Binary file not shown.
24 changes: 24 additions & 0 deletions src/helpers/medias/__tests__/helpers/make-blob-from-file.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { readFileSync } from "fs";
import { resolve } from "path";

export const makeBlobFromFile = async (
fileName: string,
mimeType: string,
): Promise<Blob> => {
const file = readFileSync(resolve(__dirname, `../files/${fileName}`));
return new Blob([file], { type: mimeType });
};

const makeUint8ArrayFromBlob = async (blob: Blob): Promise<Uint8Array> => {
const arrayBuffer = await blob.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
return new Uint8Array(buffer);
};

export const makeUint8ArrayFromFile = async (
fileName: string,
mimeType: string,
): Promise<Uint8Array> => {
const blob = await makeBlobFromFile(fileName, mimeType);
return await makeUint8ArrayFromBlob(blob);
};
51 changes: 51 additions & 0 deletions src/helpers/medias/__tests__/parse-blob-for-bluesky.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { parseBlobForBluesky } from "../parse-blob-for-bluesky.js";
import {
makeBlobFromFile,
makeUint8ArrayFromFile,
} from "./helpers/make-blob-from-file.js";

let imageBlob: Blob;
let bskyBlobData: Uint8Array;
const initBlobsForMime = async (mimeType: string) => {
const [type, extension] = mimeType.split("/");
const fileName = `${type}-${extension}.${extension}`;

bskyBlobData = await makeUint8ArrayFromFile(fileName, mimeType);
imageBlob = await makeBlobFromFile(fileName, mimeType);
};

describe("parseBlobForBluesky", () => {
describe("when the mime type is not supported", () => {
it.each`
mimeType
${"video/mp4"}
${"image/webp"}
`("should reject for $mimeType", async ({ mimeType }) => {
await initBlobsForMime(mimeType);

await expect(parseBlobForBluesky(imageBlob)).rejects.toContain(
"not supported",
);
});
});

describe("when the mime type is supported", () => {
it.each`
mimeType
${"image/jpeg"}
${"image/jpg"}
${"image/png"}
`(
"should resolve bluesky compatible blob for $mimeType",
async ({ mimeType }) => {
await initBlobsForMime(mimeType);

const result = await parseBlobForBluesky(imageBlob);
expect(result).toStrictEqual({
blobData: bskyBlobData,
mimeType,
});
},
);
});
});
2 changes: 1 addition & 1 deletion src/helpers/medias/parse-blob-for-bluesky.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ interface BlueskyBlob {
*/
export const parseBlobForBluesky = async (blob: Blob): Promise<BlueskyBlob> => {
return new Promise<BlueskyBlob>((resolve, reject) => {
const allowedMimeTypes = ["image/jpeg", "image/png"];
const allowedMimeTypes = ["image/jpeg", "image/jpg", "image/png"];
const mimeType = blob.type;

blob.arrayBuffer().then((ab) => {
Expand Down
10 changes: 10 additions & 0 deletions src/helpers/post/__tests__/get-post-excerpt.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { getPostExcerpt } from "../get-post-excerpt.js";

describe("getPostExcerpt", () => {
it("should return the 25 first characters and remove break lines", () => {
const result = getPostExcerpt(
"This is a test from\n the Potato Inc. tech lead",
);
expect(result).toBe("« This is a test from the P... »");
});
});
27 changes: 20 additions & 7 deletions src/helpers/tweet/__tests__/format-tweet-text.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,25 @@ import { Tweet } from "@the-convocation/twitter-scraper";
import { formatTweetText } from "../format-tweet-text.js";

describe("formatTweetText", () => {
it("should keep untouched a tweet without links", () => {
const tweet: Tweet = {
text: "This is a tweet without links.",
urls: [],
} as unknown as Tweet;
const result = formatTweetText(tweet);
expect(result).toStrictEqual("This is a tweet without links.");
describe("when the tweet has links", () => {
it("should remove the t.co links from the text", () => {
const tweet: Tweet = {
text: "This is a tweet with a link",
urls: ["https://t.co/abc123"],
} as unknown as Tweet;
const result = formatTweetText(tweet);
expect(result).toStrictEqual("This is a tweet with a link");
});
});

describe("when the tweet has media links", () => {
it("should remove the t.co links from the text", () => {
const tweet: Tweet = {
text: "This is a tweet with a media https://t.co/media123",
urls: [],
} as unknown as Tweet;
const result = formatTweetText(tweet);
expect(result).toStrictEqual("This is a tweet with a media");
});
});
});
54 changes: 54 additions & 0 deletions src/helpers/tweet/__tests__/get-eligible-tweet.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Tweet } from "@the-convocation/twitter-scraper";

import { getEligibleTweet } from "../get-eligible-tweet.js";
import { keepRecentTweets, keepSelfQuotes, keepSelfReplies } from "../index.js";

jest.mock("../../../constants.js", () => {
return {
TWITTER_HANDLE: "username",
};
});
jest.mock("../index.js", () => {
return {
keepSelfReplies: jest.fn(),
keepSelfQuotes: jest.fn(),
keepRecentTweets: jest.fn(),
};
});

describe("getEligibleTweet", () => {
it.each`
isNotRetweet | isSelfReply | isSelfQuote | isRecentTweet | keep
${false} | ${false} | ${false} | ${false} | ${false}
${true} | ${true} | ${false} | ${false} | ${false}
${true} | ${false} | ${true} | ${false} | ${false}
${true} | ${false} | ${false} | ${true} | ${false}
${true} | ${true} | ${true} | ${true} | ${true}
`(
"should only return keep tweet when all conditions are met",
async ({ isNotRetweet, isSelfReply, isSelfQuote, isRecentTweet, keep }) => {
// Set mocks values
(keepSelfReplies as jest.Mock).mockReturnValue(isSelfReply);
(keepSelfQuotes as jest.Mock).mockReturnValue(isSelfQuote);
(keepRecentTweets as jest.Mock).mockReturnValue(isRecentTweet);

// Run test
const result = await getEligibleTweet({
isRetweet: !isNotRetweet,
} as unknown as Tweet);

// We're only checking for the keep status
expect(result).toStrictEqual(
keep
? {
inReplyToStatus: undefined,
inReplyToStatusId: undefined,
isRetweet: !isNotRetweet,
quotedStatus: undefined,
quotedStatusId: undefined,
}
: undefined,
);
},
);
});
10 changes: 10 additions & 0 deletions src/helpers/tweet/__tests__/get-tweet-id-from-permalink.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { getTweetIdFromPermalink } from "../get-tweet-id-from-permalink.js";

describe("getTweetIdFromPermalink", () => {
it("should return the tweet id", () => {
const result = getTweetIdFromPermalink(
"https://twitter.com/username/status/1234567890123456789",
);
expect(result).toBe("1234567890123456789");
});
});
21 changes: 21 additions & 0 deletions src/helpers/tweet/__tests__/is-tweet-cached.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Tweet } from "@the-convocation/twitter-scraper";

import { isTweetCached } from "../is-tweet-cached.js";

describe("isTweetCached", () => {
it("should return true if the tweet is cached", () => {
const result = isTweetCached(
{ id: "1234567890123456789" } as unknown as Tweet,
{
"1234567890123456789": {
mastodon: "mastodonId",
bluesky: {
cid: "cid",
rkey: "rkey",
},
},
},
);
expect(result).toBe(true);
});
});
23 changes: 23 additions & 0 deletions src/helpers/tweet/__tests__/keep-recent-tweets.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Tweet } from "@the-convocation/twitter-scraper";

import { keepRecentTweets } from "../keep-recent-tweets.js";

describe("keepRecentTweets", () => {
describe("when the tweet is recent", () => {
it("should return true", () => {
const result = keepRecentTweets({
timestamp: Date.now(),
} as unknown as Tweet);
expect(result).toBe(true);
});
});

describe("when the tweet is old", () => {
it("should return false", () => {
const result = keepRecentTweets({
timestamp: new Date("1997-01-01").getTime(),
} as unknown as Tweet);
expect(result).toBe(false);
});
});
});
37 changes: 37 additions & 0 deletions src/helpers/tweet/__tests__/keep-self-quotes.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Tweet } from "@the-convocation/twitter-scraper";

import { keepSelfQuotes } from "../keep-self-quotes.js";

jest.mock("../../../constants.js", () => {
return {
TWITTER_HANDLE: "username",
};
});

describe("keepSelfQuotes", () => {
describe("when the tweet is a quote", () => {
it("should return true when is from the same user", async () => {
const result = await keepSelfQuotes({
quotedStatus: { username: "username" },
} as unknown as Tweet);

expect(result).toBe(true);
});

it("should return false when is from a different user", async () => {
const result = await keepSelfQuotes({
quotedStatus: { username: "potatoinc" },
} as unknown as Tweet);

expect(result).toBe(false);
});
});

describe("when the tweet is not a quote", () => {
it("should return true when is from the same user", async () => {
const result = await keepSelfQuotes({} as unknown as Tweet);

expect(result).toBe(true);
});
});
});
37 changes: 37 additions & 0 deletions src/helpers/tweet/__tests__/keep-self-replies.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Tweet } from "@the-convocation/twitter-scraper";

import { keepSelfReplies } from "../keep-self-replies.js";

jest.mock("../../../constants.js", () => {
return {
TWITTER_HANDLE: "username",
};
});

describe("keepSelfReplies", () => {
describe("when the tweet is a reply", () => {
it("should return true when is to the same user", async () => {
const result = await keepSelfReplies({
inReplyToStatus: { username: "username" },
} as unknown as Tweet);

expect(result).toBe(true);
});

it("should return false when is to a different user", async () => {
const result = await keepSelfReplies({
inReplyToStatus: { username: "potatoinc" },
} as unknown as Tweet);

expect(result).toBe(false);
});
});

describe("when the tweet is not a reply", () => {
it("should return true when is from the same user", async () => {
const result = await keepSelfReplies({} as unknown as Tweet);

expect(result).toBe(true);
});
});
});
4 changes: 2 additions & 2 deletions src/helpers/tweet/format-tweet-text.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Tweet } from "@the-convocation/twitter-scraper";

export const formatTweetText = (tweet: Tweet): string => {
let text = tweet.text || "";
let text = tweet.text ?? "";

// Replace urls
tweet.urls.forEach((url) => {
Expand All @@ -12,5 +12,5 @@ export const formatTweetText = (tweet: Tweet): string => {
text = text.replaceAll(/https:\/\/t\.co\/\w+/g, "");

// Return formatted
return text;
return text.trim();
};
2 changes: 1 addition & 1 deletion src/helpers/tweet/get-eligible-tweet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const getEligibleTweet = async (
if (DEBUG && keep) {
console.log(
`✅ : ${tweet.id}: from:@${tweet.username}: ${getPostExcerpt(
tweet.text || "",
tweet.text ?? "",
)}`,
);
}
Expand Down
16 changes: 16 additions & 0 deletions src/helpers/url/__tests__/get-redirections.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { getRedirectedUrl } from "../get-redirection.js";

describe("getRedirectedUrl", () => {
describe("when the url is redirected", () => {
it("should return the final url", async () => {
const result = await getRedirectedUrl("https://t.co/bbJgfyzcJR");
expect(result).toStrictEqual("https://github.com/");
});
});
describe("when the url is not redirected", () => {
it("should return null", async () => {
const result = await getRedirectedUrl("https://t.co/_____null_____");
expect(result).toStrictEqual(null);
});
});
});
Loading