Skip to content

Commit

Permalink
fix: updated parsing for non browser calls
Browse files Browse the repository at this point in the history
- updated non browser calls
- added jest config and test
  • Loading branch information
rohit1901 committed Feb 7, 2025
1 parent 1b8456b commit 11f249e
Show file tree
Hide file tree
Showing 6 changed files with 3,598 additions and 794 deletions.
11 changes: 11 additions & 0 deletions jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
require("tsconfig-paths/register");

// jest.config.js
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
testMatch: ["**/__tests__/**/*.test.ts"],
moduleNameMapper: {
"^@/(.*)$": "<rootDir>/src/$1",
},
};
151 changes: 151 additions & 0 deletions lib/__tests__/app.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import { getFeedByLink, getPosts, getSubstackFeed } from "../main";
import { RawFeedChannel } from "../types";

describe("getSubstackFeed", () => {
it("should throw an error for invalid Substack feed", async () => {
const invalidFeedUrl = "https://example.com/invalid-feed";
await expect(getSubstackFeed(invalidFeedUrl)).rejects.toThrow(
"Error occurred fetching Feed from Substack",
);
});
});

describe("getFeedByLink", () => {
it("should throw an error for incorrect feed format", () => {
const invalidFeed = { invalid: "format" };
expect(() => getFeedByLink(invalidFeed, "https://example.com")).toThrow(
"Feed is not in the correct format",
);
});
});

describe("getPosts", () => {
const mockChannels: RawFeedChannel[] = [
{
title: ["Post 1"],
description: ["Description 1"],
link: ["https://example.com/post1"],
image: [
{
url: ["https://example.com/image1"],
title: ["Image 1"],
link: ["https://example.com/image1"],
},
],
generator: ["Substack"],
lastBuildDate: ["2023-01-01"],
"atom:link": [
{
$: {
href: "https://example.com/feed",
rel: "self",
type: "application/rss+xml",
},
},
],
copyright: ["© 2023"],
language: ["en"],
webMaster: ["some"],
"itunes:owner": [
{ "itunes:email": ["some@some.com"], "itunes:name": ["Some Name"] },
],
"itunes:author": ["Some Author"],
"googleplay:owner": ["Some Owner"],
"googleplay:email": ["some@some.com"],
"googleplay:author": ["Some Author"],
item: [
{
title: ["Post 1"],
description: ["Description 1"],
link: ["https://example.com/post1"],
guid: [
{ _: "https://example.com/post1", $: { isPermaLink: "true" } },
],
"dc:creator": ["Some Author"],
pubDate: ["2023-01-01"],
enclosure: [
{
$: {
url: "https://example.com/image1",
length: "123",
type: "image/jpeg",
},
},
],
"content:encoded": ["<p>Content 1</p>"],
},
],
},
{
title: ["Post 2"],
description: ["Description 2"],
link: ["https://example.com/post1"],
image: [
{
url: ["https://example.com/image1"],
title: ["Image 1"],
link: ["https://example.com/image1"],
},
],
generator: ["Substack"],
lastBuildDate: ["2023-01-01"],
"atom:link": [
{
$: {
href: "https://example.com/feed",
rel: "self",
type: "application/rss+xml",
},
},
],
copyright: ["© 2023"],
language: ["en"],
webMaster: ["some"],
"itunes:owner": [
{ "itunes:email": ["some@some.com"], "itunes:name": ["Some Name"] },
],
"itunes:author": ["Some Author"],
"googleplay:owner": ["Some Owner"],
"googleplay:email": ["some@some.com"],
"googleplay:author": ["Some Author"],
item: [
{
title: ["Post 1"],
description: ["Description 1"],
link: ["https://example.com/post1"],
guid: [
{ _: "https://example.com/post1", $: { isPermaLink: "true" } },
],
"dc:creator": ["Some Author"],
pubDate: ["2023-01-01"],
enclosure: [
{
$: {
url: "https://example.com/image1",
length: "123",
type: "image/jpeg",
},
},
],
"content:encoded": ["<p>Content 1</p>"],
},
],
},
];

it("should transform raw items to SubstackItems", () => {
const result = getPosts(mockChannels);
expect(result).toHaveLength(1);
expect(result[0]).toEqual({
title: "Post 1",
description: "Description 1",
link: "https://example.com/post1",
pubDate: "2023-01-01",
content: "<p>Content 1</p>",
});
});

it("should handle empty channels array", () => {
expect(() => getPosts([])).toThrow();
});
});
8 changes: 5 additions & 3 deletions lib/main.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as parser from "xml2js";
import { isRawFeed, isRawFeedChannel } from "./typeguards";
import { isRawFeed, isRawFeedChannel, isValidSubstackFeed } from "./typeguards";
import { RawFeedChannel, RawItem, SubstackItem } from "./types";

const CORS_PROXY = "https://api.allorigins.win/get?url=";
Expand All @@ -12,7 +12,7 @@ const getRawXMLSubstackFeed = async (feedUrl: string, proxy = false) => {
? `${CORS_PROXY}${encodeURIComponent(feedUrl)}`
: feedUrl;
const promise = await fetch(path);
if (promise.ok) return promise.text();
if (promise.ok) return promise.json();
} catch (e) {
throw new Error("Error occurred fetching Feed from Substack");
}
Expand Down Expand Up @@ -44,8 +44,10 @@ export const getSubstackFeed = async (
callback?: (err: Error | null, result: unknown) => void,
): Promise<string | undefined> => {
const rawXML = await getRawXMLSubstackFeed(feedUrl, proxy);
if (!isValidSubstackFeed(rawXML))
throw new Error("Error occurred fetching Feed from Substack");
if (!callback) return rawXML;
await parseXML(rawXML, callback);
await parseXML(rawXML.contents, callback);
};
export const getFeedByLink = (
rawFeed: unknown,
Expand Down
4 changes: 4 additions & 0 deletions lib/typeguards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,7 @@ export const isEnclosure = (data: any): data is Enclosure => {
typeof data.$.type === "string"
);
};

export const isValidSubstackFeed = (data: any): boolean => {
return data && data.contents && data.status.http_code == 200;
};
Loading

0 comments on commit 11f249e

Please sign in to comment.