Skip to content

Commit 0588557

Browse files
committed
Added jest and playwright, added tests
1 parent 20d0eee commit 0588557

20 files changed

+1933
-97
lines changed

apps/contact/.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,10 @@ yarn-error.log*
3939
# typescript
4040
*.tsbuildinfo
4141
next-env.d.ts
42+
43+
# Playwright
44+
node_modules/
45+
/test-results/
46+
/playwright-report/
47+
/blob-report/
48+
/playwright/.cache/

apps/contact/app/api/contact/route.ts

Lines changed: 3 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { notifyContactCreated } from "../../../utils/notifyContactCreated";
12
import { Client, isFullPage } from "@notionhq/client";
23
import { nanoid } from "nanoid";
34
import z from "zod";
@@ -16,97 +17,11 @@ const bodyValidationSchema = z.object({
1617

1718
type RequestBody = z.infer<typeof bodyValidationSchema>;
1819

19-
const {
20-
NOTION_TOKEN,
21-
SLACK_CHANNEL,
22-
SLACK_BOT_TOKEN,
23-
MENTION_EMAILS,
24-
MENTION_IDS,
25-
NOTION_DATABASE_ID,
26-
IS_OFFLINE,
27-
} = process.env;
20+
const { NOTION_TOKEN, MENTION_EMAILS, MENTION_IDS, NOTION_DATABASE_ID } =
21+
process.env;
2822

2923
const notion = new Client({ auth: NOTION_TOKEN });
3024

31-
const createPayload = (name: string, email: string, url: string) => ({
32-
channel: SLACK_CHANNEL,
33-
blocks: [
34-
{
35-
type: "header",
36-
text: {
37-
type: "plain_text",
38-
text: "We have 1 new message(s).",
39-
emoji: true,
40-
},
41-
},
42-
{
43-
type: "section",
44-
text: {
45-
type: "mrkdwn",
46-
text: `We got a new message from _${name}_ (_${email}_).`,
47-
},
48-
},
49-
{
50-
type: "divider",
51-
},
52-
{
53-
type: "section",
54-
text: {
55-
type: "mrkdwn",
56-
text: " ",
57-
},
58-
accessory: {
59-
type: "button",
60-
text: {
61-
type: "plain_text",
62-
text: "Show me the message",
63-
emoji: true,
64-
},
65-
value: "new_message_click",
66-
url,
67-
action_id: "button-action",
68-
},
69-
},
70-
],
71-
});
72-
73-
const notifyContactCreated = async (
74-
name: string,
75-
email: string,
76-
url: string,
77-
) => {
78-
const payload = createPayload(name, email, url);
79-
const payloadStringify = JSON.stringify(payload);
80-
81-
if (IS_OFFLINE) {
82-
console.log(payload);
83-
} else {
84-
try {
85-
const result = await fetch("https://slack.com/api/chat.postMessage", {
86-
method: "POST",
87-
body: payloadStringify,
88-
headers: {
89-
"Content-Type": "application/json; charset=utf-8",
90-
"Content-Length": payloadStringify.length.toString(),
91-
Authorization: `Bearer ${SLACK_BOT_TOKEN}`,
92-
Accept: "application/json",
93-
},
94-
});
95-
if (result.status !== 200) {
96-
throw {
97-
statusCode: result.status,
98-
headers: {
99-
"Access-Control-Allow-Origin": "*",
100-
"Access-Control-Allow-Credentials": true,
101-
},
102-
};
103-
}
104-
} catch (error) {
105-
throw error;
106-
}
107-
}
108-
};
109-
11025
const mentionPerson = ({ id, email }: { id: string; email: string }) => [
11126
{
11227
mention: {

apps/contact/jest.config.ts

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
// /**
2+
// * For a detailed explanation regarding each configuration property, visit:
3+
// * https://jestjs.io/docs/configuration
4+
// */
5+
6+
// /** @type {import('jest').Config} */
7+
// const config = {
8+
// // All imported modules in your tests should be mocked automatically
9+
// // automock: false,
10+
11+
// // Stop running tests after `n` failures
12+
// // bail: 0,
13+
14+
// // The directory where Jest should store its cached dependency information
15+
// // cacheDirectory: "/tmp/jest_rs",
16+
17+
// // Automatically clear mock calls, instances, contexts and results before every test
18+
// // clearMocks: false,
19+
20+
// // Indicates whether the coverage information should be collected while executing the test
21+
// // collectCoverage: false,
22+
23+
// // An array of glob patterns indicating a set of files for which coverage information should be collected
24+
// // collectCoverageFrom: undefined,
25+
26+
// // The directory where Jest should output its coverage files
27+
// // coverageDirectory: undefined,
28+
29+
// // An array of regexp pattern strings used to skip coverage collection
30+
// // coveragePathIgnorePatterns: [
31+
// // "/node_modules/"
32+
// // ],
33+
34+
// // Indicates which provider should be used to instrument code for coverage
35+
// coverageProvider: "v8",
36+
37+
// // A list of reporter names that Jest uses when writing coverage reports
38+
// // coverageReporters: [
39+
// // "json",
40+
// // "text",
41+
// // "lcov",
42+
// // "clover"
43+
// // ],
44+
45+
// // An object that configures minimum threshold enforcement for coverage results
46+
// // coverageThreshold: undefined,
47+
48+
// // A path to a custom dependency extractor
49+
// // dependencyExtractor: undefined,
50+
51+
// // Make calling deprecated APIs throw helpful error messages
52+
// // errorOnDeprecated: false,
53+
54+
// // The default configuration for fake timers
55+
// // fakeTimers: {
56+
// // "enableGlobally": false
57+
// // },
58+
59+
// // Force coverage collection from ignored files using an array of glob patterns
60+
// // forceCoverageMatch: [],
61+
62+
// // A path to a module which exports an async function that is triggered once before all test suites
63+
// // globalSetup: undefined,
64+
65+
// // A path to a module which exports an async function that is triggered once after all test suites
66+
// // globalTeardown: undefined,
67+
68+
// // A set of global variables that need to be available in all test environments
69+
// globals: {
70+
// "ts-jest": {
71+
// useESM: true,
72+
// },
73+
// },
74+
75+
// // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
76+
// // maxWorkers: "50%",
77+
78+
// // An array of directory names to be searched recursively up from the requiring module's location
79+
// // moduleDirectories: [
80+
// // "node_modules"
81+
// // ],
82+
83+
// // An array of file extensions your modules use
84+
// moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json"],
85+
// // moduleFileExtensions: [
86+
// // "js",
87+
// // "mjs",
88+
// // "cjs",
89+
// // "jsx",
90+
// // "ts",
91+
// // "mts",
92+
// // "cts",
93+
// // "tsx",
94+
// // "json",
95+
// // "node"
96+
// // ],
97+
98+
// // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
99+
// moduleNameMapper: {
100+
// "^(\\.{1,2}/.*)\\.js$": "$1",
101+
// },
102+
103+
// // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
104+
// // modulePathIgnorePatterns: [],
105+
106+
// // Activates notifications for test results
107+
// // notify: false,
108+
109+
// // An enum that specifies notification mode. Requires { notify: true }
110+
// // notifyMode: "failure-change",
111+
112+
// // A preset that is used as a base for Jest's configuration
113+
// preset: "ts-jest/presets/default-esm",
114+
115+
// // Run tests from one or more projects
116+
// // projects: undefined,
117+
118+
// // Use this configuration option to add custom reporters to Jest
119+
// // reporters: undefined,
120+
121+
// // Automatically reset mock state before every test
122+
// // resetMocks: false,
123+
124+
// // Reset the module registry before running each individual test
125+
// // resetModules: false,
126+
127+
// // A path to a custom resolver
128+
// // resolver: undefined,
129+
130+
// // Automatically restore mock state and implementation before every test
131+
// // restoreMocks: false,
132+
133+
// // The root directory that Jest should scan for tests and modules within
134+
// // rootDir: undefined,
135+
136+
// // A list of paths to directories that Jest should use to search for files in
137+
// // roots: [
138+
// // "<rootDir>"
139+
// // ],
140+
141+
// // Allows you to use a custom runner instead of Jest's default test runner
142+
// // runner: "jest-runner",
143+
144+
// // The paths to modules that run some code to configure or set up the testing environment before each test
145+
// // setupFiles: ["./jest.setup.ts"],
146+
147+
// // A list of paths to modules that run some code to configure or set up the testing framework before each test
148+
// setupFilesAfterEnv: ["<rootDir>/jest.setup.ts"],
149+
150+
// // The number of seconds after which a test is considered as slow and reported as such in the results.
151+
// // slowTestThreshold: 5,
152+
153+
// // A list of paths to snapshot serializer modules Jest should use for snapshot testing
154+
// // snapshotSerializers: [],
155+
156+
// // The test environment that will be used for testing
157+
// testEnvironment: "node",
158+
159+
// // Options that will be passed to the testEnvironment
160+
// testEnvironmentOptions: {
161+
// customExportConditions: ["react-native"],
162+
// },
163+
164+
// // Adds a location field to test results
165+
// // testLocationInResults: false,
166+
167+
// // The glob patterns Jest uses to detect test files
168+
// // testMatch: [
169+
// // "**/__tests__/**/*.?([mc])[jt]s?(x)",
170+
// // "**/?(*.)+(spec|test).?([mc])[jt]s?(x)"
171+
// // ],
172+
173+
// // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
174+
// // testPathIgnorePatterns: [
175+
// // "/node_modules/"
176+
// // ],
177+
178+
// // The regexp pattern or array of patterns that Jest uses to detect test files
179+
// // testRegex: [],
180+
181+
// // This option allows the use of a custom results processor
182+
// // testResultsProcessor: undefined,
183+
184+
// // This option allows use of a custom test runner
185+
// // testRunner: "jest-circus/runner",
186+
187+
// // A map from regular expressions to paths to transformers
188+
// transform: {},
189+
190+
// // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
191+
// transformIgnorePatterns: [
192+
// "/node_modules/(?!nanoid|undici)/", // transform nanoid and undici
193+
// ],
194+
195+
// // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
196+
// // unmockedModulePathPatterns: undefined,
197+
198+
// // Indicates whether each individual test should be reported during the run
199+
// // verbose: undefined,
200+
201+
// // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
202+
// // watchPathIgnorePatterns: [],
203+
204+
// // Whether to use watchman for file crawling
205+
// // watchman: true,
206+
207+
// extensionsToTreatAsEsm: [".ts"],
208+
// };
209+
210+
// module.exports = config;
211+
212+
import type { Config } from "jest";
213+
214+
const config: Config = {
215+
preset: "ts-jest/presets/js-with-ts",
216+
testEnvironment: "node",
217+
extensionsToTreatAsEsm: [".ts"],
218+
transform: {
219+
"^.+\\.tsx?$": [
220+
"ts-jest",
221+
{
222+
useESM: false,
223+
tsconfig: "tsconfig.json",
224+
},
225+
],
226+
},
227+
transformIgnorePatterns: [
228+
"/node_modules/(?!(nanoid)/)", // ensure nanoid gets transformed
229+
],
230+
moduleFileExtensions: ["cjs", "js", "ts", "tsx", "json", "node"],
231+
setupFilesAfterEnv: ["./jest.setup.ts"],
232+
testPathIgnorePatterns: ["/node_modules/", "/tests/e2e/"],
233+
};
234+
235+
export default config;

apps/contact/jest.setup.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Step 1: Polyfill encoders
2+
import { TextEncoder, TextDecoder } from "util";
3+
(globalThis as any).TextEncoder = TextEncoder;
4+
(globalThis as any).TextDecoder = TextDecoder;
5+
6+
// Step 2: Polyfill ReadableStream
7+
import { ReadableStream } from "web-streams-polyfill";
8+
(globalThis as any).ReadableStream = ReadableStream;
9+
10+
// Step 3: Polyfill Web Fetch APIs using undici
11+
import { fetch, Response, Request, Headers } from "undici";
12+
(globalThis as any).fetch = fetch;
13+
(globalThis as any).Response = Response;
14+
(globalThis as any).Request = Request;
15+
(globalThis as any).Headers = Headers;
16+
17+
if (typeof clearImmediate === "undefined") {
18+
(global as any).clearImmediate = (id: any) => {
19+
return clearTimeout(id);
20+
};
21+
}
22+
23+
if (typeof setImmediate === "undefined") {
24+
(global as any).setImmediate = (fn: Function, ...args: any[]) => {
25+
return setTimeout(fn, 0, ...args);
26+
};
27+
}
28+
29+
afterEach(async () => {
30+
jest.resetAllMocks();
31+
});

0 commit comments

Comments
 (0)