Skip to content

Commit

Permalink
Massive change to get functions to run ESM so we can use unified ther…
Browse files Browse the repository at this point in the history
…e (eyeroll). Involves upgrading runtime to node 20, using js extension for all ts relative imports (eyeroll), and a host of other smaller config changes
  • Loading branch information
mdirolf committed May 27, 2024
1 parent 1407361 commit 5e3610e
Show file tree
Hide file tree
Showing 218 changed files with 4,221 additions and 2,534 deletions.
8 changes: 8 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,12 @@
"trailingComma": "es5",
"useTabs": false,
"vueIndentScriptAndStyle": false,
"overrides": [
{
"files": "*.ts",
"options": {
"parser": "typescript"
}
}
]
}
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM mcr.microsoft.com/playwright:v1.39.0-jammy as dev
FROM mcr.microsoft.com/playwright:v1.44.1-jammy as dev
RUN mkdir /src
WORKDIR /src
COPY app/package.json app/yarn.lock app/lingui.config.ts ./
Expand All @@ -15,7 +15,7 @@ RUN firebase setup:emulators:pubsub
ARG COMMIT=dev
ENV NEXT_PUBLIC_COMMIT_HASH $COMMIT

FROM node:18-slim as builder
FROM node:20-slim as builder
RUN mkdir /src
WORKDIR /src
COPY app/package.json app/yarn.lock ./
Expand All @@ -32,7 +32,7 @@ RUN yarn install --production --ignore-scripts --prefer-offline
ARG COMMIT
RUN test -n "$COMMIT"

FROM gcr.io/distroless/nodejs18-debian11 as prod
FROM gcr.io/distroless/nodejs20-debian12 as prod
ARG COMMIT
ENV NEXT_PUBLIC_COMMIT_HASH=$COMMIT NODE_ENV=production NEXT_TELEMETRY_DISABLED=1
WORKDIR /app
Expand Down
4 changes: 2 additions & 2 deletions RUNNING_CONTAINERLESS.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ Now click "Continue to console" and click "Service accounts" at the top of the P

### Install dependencies

Crosshare is currently deployed on node 18 - on Fedora it's:
Crosshare is currently deployed on node 20 - on Fedora it's:

```shell
$ sudo dnf module install nodejs:18
$ sudo dnf install nodejs20
```

We use `yarn` for package management:
Expand Down
5 changes: 4 additions & 1 deletion app/.prettierignore
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
**/locales/**
**/locales/**

app/lib/spam.ts
lib/spam.ts
122 changes: 63 additions & 59 deletions app/__tests__/Builder.test.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
import * as firebaseTesting from '@firebase/rules-unit-testing';
import { jest } from '@jest/globals';
import type firebase from 'firebase/compat/app';
import type firebaseAdminType from 'firebase-admin';
import NextJSRouter from 'next/router';
import React from 'react';
import waitForExpect from 'wait-for-expect';
import { getDateString, prettifyDateString } from '../lib/dbtypes.js';
import { setApp } from '../lib/firebaseWrapper.js';
import { getPuzzlePageProps as getServerSideProps } from '../lib/serverOnly.js';
import {
getByLabelText,
getUser,
RenderResult,
act,
cleanup,
render,
fireEvent,
RenderResult,
getByLabelText,
getProps,
act,
getUser,
render,
waitFor,
} from '../lib/testingUtils';
import BuilderPage from '../pages/construct';
import { setApp } from '../lib/firebaseWrapper';
import type firebaseAdminType from 'firebase-admin';
import * as firebaseTesting from '@firebase/rules-unit-testing';
import type firebase from 'firebase/compat/app';
import NextJSRouter from 'next/router';
import PuzzlePage from '../pages/crosswords/[[...puzzleId]]';
import { getPuzzlePageProps as getServerSideProps } from '../lib/serverOnly';
import { PuzzleLoader as StatsPuzzleLoader } from '../pages/stats/[puzzleId]';
import waitForExpect from 'wait-for-expect';
import { getDateString, prettifyDateString } from '../lib/dbtypes';
import { jest } from '@jest/globals';
} from '../lib/testingUtils.js';
import BuilderPage from '../pages/construct.js';
import PuzzlePage from '../pages/crosswords/[[...puzzleId]].js';
import { PuzzleLoader as StatsPuzzleLoader } from '../pages/stats/[puzzleId].js';

/*jest.mock(
'next/link',
Expand Down Expand Up @@ -100,7 +100,7 @@ test('puzzle in progress should be cached in local storage', async () => {
sessionStorage.clear();
localStorage.clear();

setApp(app as firebase.app.App);
setApp(app);

let r = render(<BuilderPage />, { user: mike, displayName: 'Mike' });
let launchButton = (await r.findAllByText('Launch Constructor'))[0];
Expand Down Expand Up @@ -151,7 +151,7 @@ async function publishPuzzle(
.doc('donations')
.set({ d: [] });

setApp(app as firebase.app.App);
setApp(app);

const r = render(<BuilderPage />, { user: mike, displayName: 'Mike' });
const launchButton = (await r.findAllByText('Launch Constructor'))[0];
Expand Down Expand Up @@ -231,7 +231,7 @@ test('moderate as daily mini', async () => {
}

// The puzzle should be visible to an admin w/ moderation links
setApp(adminUserApp as firebase.app.App);
setApp(adminUserApp);
const r4 = render(<PuzzlePage {...props1} />, {
user: miked,
isAdmin: true,
Expand All @@ -242,14 +242,18 @@ test('moderate as daily mini', async () => {
fireEvent.click(r4.getByText(/Moderate/i));
await r4.findByText(/Schedule as Daily Mini/i);
await waitFor(
() => expect(r4.getByTestId('today-button')).not.toBeDisabled(),
() => {
expect(r4.getByTestId('today-button')).not.toBeDisabled();
},
{
timeout: 5000,
}
);
fireEvent.click(r4.getByTestId('today-button'));
await waitFor(
() => expect(r4.getByText(/Schedule as Daily Mini/i)).not.toBeDisabled(),
() => {
expect(r4.getByText(/Schedule as Daily Mini/i)).not.toBeDisabled();
},
{
timeout: 5000,
}
Expand All @@ -268,12 +272,12 @@ test('moderate as daily mini', async () => {
}
const ds = getDateString(new Date());
expect(res.docs[0]?.id).toEqual(puzzleId);
expect(updated['m']).toEqual(true);
expect(updated['f']).toEqual(undefined);
expect(updated['p']).not.toEqual(null);
expect(updated['c']).toEqual('dailymini');
expect(updated['dmd']).toEqual(prettifyDateString(ds));
expect(updated['t']).toEqual('Our Title');
expect(updated.m).toEqual(true);
expect(updated.f).toEqual(undefined);
expect(updated.p).not.toEqual(null);
expect(updated.c).toEqual('dailymini');
expect(updated.dmd).toEqual(prettifyDateString(ds));
expect(updated.t).toEqual('Our Title');
});

test('publish as default', async () => {
Expand All @@ -293,14 +297,14 @@ test('publish as default', async () => {
throw new Error();
}
const puzzleId = puzzles.docs[0]?.id;
expect(puzzle['m']).toEqual(false);
expect(puzzle['p']).not.toEqual(null);
expect(puzzle['c']).toEqual(null);
expect(puzzle['t']).toEqual('Our Title');
expect(puzzle['pvu']).toBeTruthy();
await waitForExpect(async () =>
expect(NextJSRouter.push).toHaveBeenCalledTimes(1)
);
expect(puzzle.m).toEqual(false);
expect(puzzle.p).not.toEqual(null);
expect(puzzle.c).toEqual(null);
expect(puzzle.t).toEqual('Our Title');
expect(puzzle.pvu).toBeTruthy();
await waitForExpect(async () => {
expect(NextJSRouter.push).toHaveBeenCalledTimes(1);
});
expect(NextJSRouter.push).toHaveBeenCalledWith(
`/crosswords/${puzzles.docs[0]?.id}/our-title`
);
Expand All @@ -322,7 +326,7 @@ test('publish as default', async () => {
cleanup();

// The puzzle should be visible on the puzzle page, even to a rando
setApp(serverApp as firebase.app.App);
setApp(serverApp);
const props1 = await getProps(
await getServerSideProps({
params: { puzzleId: [puzzleId, 'our-title'] },
Expand All @@ -333,7 +337,7 @@ test('publish as default', async () => {
if (!props1) {
throw new Error('bad props');
}
setApp(randoApp as firebase.app.App);
setApp(randoApp);

const r5 = render(<PuzzlePage {...props1} />, {
user: rando,
Expand All @@ -352,7 +356,7 @@ test('publish as default', async () => {
cleanup();

// The puzzle should be visible to an admin w/ moderation links
setApp(adminUserApp as firebase.app.App);
setApp(adminUserApp);
const r4 = render(<PuzzlePage {...props1} />, {
user: miked,
isAdmin: true,
Expand All @@ -377,11 +381,11 @@ test('publish as default', async () => {
throw new Error();
}
expect(res.docs[0]?.id).toEqual(puzzleId);
expect(updated['m']).toEqual(true);
expect(updated['f']).toEqual(true);
expect(updated['p']).not.toEqual(null);
expect(updated['c']).toEqual(null);
expect(updated['t']).toEqual('Our Title');
expect(updated.m).toEqual(true);
expect(updated.f).toEqual(true);
expect(updated.p).not.toEqual(null);
expect(updated.c).toEqual(null);
expect(updated.t).toEqual('Our Title');
});

test('publish custom / non-rectangular size', async () => {
Expand All @@ -395,7 +399,7 @@ test('publish custom / non-rectangular size', async () => {
.doc('donations')
.set({ d: [] });

setApp(app as firebase.app.App);
setApp(app);

const r = render(<BuilderPage />, { user: mike, displayName: 'Mike' });
const launchButton = (await r.findAllByText('Launch Constructor'))[0];
Expand Down Expand Up @@ -459,25 +463,25 @@ test('publish custom / non-rectangular size', async () => {
throw new Error();
}
const puzzleId = puzzles.docs[0]?.id;
expect(puzzle['m']).toEqual(false);
expect(puzzle['p']).not.toEqual(null);
expect(puzzle['c']).toEqual(null);
expect(puzzle['t']).toEqual('Our Title');
expect(puzzle['bp']).toEqual('Here is our new blog post');
expect(puzzle['pv']).toBeUndefined();
expect(puzzle['pvu']).toBeTruthy();

await waitForExpect(async () =>
expect(NextJSRouter.push).toHaveBeenCalledTimes(1)
);
expect(puzzle.m).toEqual(false);
expect(puzzle.p).not.toEqual(null);
expect(puzzle.c).toEqual(null);
expect(puzzle.t).toEqual('Our Title');
expect(puzzle.bp).toEqual('Here is our new blog post');
expect(puzzle.pv).toBeUndefined();
expect(puzzle.pvu).toBeTruthy();

await waitForExpect(async () => {
expect(NextJSRouter.push).toHaveBeenCalledTimes(1);
});
expect(NextJSRouter.push).toHaveBeenCalledWith(
`/crosswords/${puzzles.docs[0]?.id}/our-title`
);

cleanup();

// The puzzle should be visible on the puzzle page, even to a rando
setApp(serverApp as firebase.app.App);
setApp(serverApp);
const props1 = await getProps(
await getServerSideProps({
params: { puzzleId: [puzzleId, 'our-title'] },
Expand All @@ -488,7 +492,7 @@ test('publish custom / non-rectangular size', async () => {
if (!props1) {
throw new Error('bad props');
}
setApp(randoApp as firebase.app.App);
setApp(randoApp);
const r5 = render(<PuzzlePage {...props1} />, {
user: rando,
displayName: 'Mike',
Expand Down
8 changes: 4 additions & 4 deletions app/__tests__/ClueText.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ClueText } from '../components/ClueText';
import { GridContext } from '../components/GridContext';
import { act, render } from '../lib/testingUtils';
import { CluedGrid, addClues, fromCells } from '../lib/viewableGrid';
import { ClueText } from '../components/ClueText.js';
import { GridContext } from '../components/GridContext.js';
import { act, render } from '../lib/testingUtils.js';
import { CluedGrid, addClues, fromCells } from '../lib/viewableGrid.js';

/*
"What's a Grecian ___?" "About $25 a week!"
Expand Down
19 changes: 9 additions & 10 deletions app/__tests__/ConstructorPage.test.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import * as firebaseTesting from '@firebase/rules-unit-testing';

import { setApp, setAdminApp } from '../lib/firebaseWrapper';
import { getUser, render, cleanup, fireEvent } from '../lib/testingUtils';
import { getMockedPuzzle } from '../lib/getMockedPuzzle';
import { AccountPage } from '../pages/account';
import waitForExpect from 'wait-for-expect';
import { setAdminApp, setApp } from '../lib/firebaseWrapper.js';
import { getMockedPuzzle } from '../lib/getMockedPuzzle.js';
import { cleanup, fireEvent, getUser, render } from '../lib/testingUtils.js';
import { AccountPage } from '../pages/account.js';
jest.mock('../lib/firebaseWrapper');
import type firebase from 'firebase/compat/app';
import { userIdToPage } from '../lib/serverOnly';
import { userIdToPage } from '../lib/serverOnly.js';
import firebaseAdmin from 'firebase-admin';

jest.mock(
'next/link',
() =>
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
// @ts-expect-error
({ children }) =>
children
); // https://github.com/vercel/next.js/issues/16864
Expand Down Expand Up @@ -85,13 +84,13 @@ test('create constructor page', async () => {
fireEvent.click(r.getByText('Create', { exact: true }));
await r.findByText('Created successfully!', undefined, { timeout: 3000 });

await waitForExpect(async () =>
await waitForExpect(async () => {
expect(
(
await adminApp.firestore().collection('cp').doc('therealmiked').get()
).data()?.u
).toEqual('mikeuserid')
);
).toEqual('mikeuserid');
});

cleanup();

Expand Down
14 changes: 6 additions & 8 deletions app/__tests__/FollowButton.test.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,23 @@
import * as firebaseTesting from '@firebase/rules-unit-testing';

import { setApp } from '../lib/firebaseWrapper';
import { setApp } from '../lib/firebaseWrapper.js';
import {
fireEvent,
render,
getUser,
render,
renderWithSnackbar,
} from '../lib/testingUtils';
import { FollowButton } from '../components/FollowButton';
} from '../lib/testingUtils.js';
import { FollowButton } from '../components/FollowButton.js';
jest.mock('../lib/firebaseWrapper');
import type firebase from 'firebase/compat/app';
import type firebaseAdminType from 'firebase-admin';

import { ConstructorPageT } from '../lib/constructorPage';
import { ConstructorPageT } from '../lib/constructorPage.js';

const projectId = 'followtests';
let adminApp: firebaseAdminType.app.App;

beforeAll(() => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
// @ts-expect-error
adminApp = firebaseTesting.initializeAdminApp({ projectId });
});

Expand Down
12 changes: 6 additions & 6 deletions app/__tests__/Markdown.test.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { GridContext } from '../components/GridContext';
import { Markdown } from '../components/Markdown';
import { markdownToHast, removeSpoilers } from '../lib/markdown/markdown';
import { render, waitFor } from '../lib/testingUtils';
import { Direction } from '../lib/types';
import { CluedGrid, addClues, fromCells } from '../lib/viewableGrid';
import { GridContext } from '../components/GridContext.js';
import { Markdown } from '../components/Markdown.js';
import { markdownToHast, removeSpoilers } from '../lib/markdown/markdown.js';
import { render, waitFor } from '../lib/testingUtils.js';
import { Direction } from '../lib/types.js';
import { CluedGrid, addClues, fromCells } from '../lib/viewableGrid.js';

test('remove spoilers', () => {
expect(removeSpoilers('Testing **something bold**')).toMatch(
Expand Down
Loading

0 comments on commit 5e3610e

Please sign in to comment.