Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 4 additions & 19 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,24 +98,6 @@ jobs:
with:
file: ./coverage.xml

build-nextjs-frontend:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4

- uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4
with:
node-version: "^20"
cache: yarn
cache-dependency-path: yarn.lock

- name: Install dependencies
run: yarn install

- name: Build Next.js frontend
run: yarn workspace main build

javascript-tests:
runs-on: ubuntu-latest
steps:
Expand Down Expand Up @@ -144,8 +126,11 @@ jobs:
- name: Lints
run: yarn run lint-check

- name: Build Next.js frontend
run: yarn workspace main build
# do this before typecheck. See https://github.com/vercel/next.js/issues/53959#issuecomment-1735563224

- name: Typecheck
if: false # To be reinstated. Anything actually used in `main` is typecheck during build
run: yarn run typecheck

- name: Get number of CPU cores
Expand Down
2 changes: 2 additions & 0 deletions frontends/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
"sideEffects": false,
"exports": {
".": "./src/generated/v1/api.ts",
"./clients": "./src/clients.ts",
"./v0": "./src/generated/v0/api.ts",
"./v1": "./src/generated/v1/api.ts",
"./hooks/*": "./src/hooks/*/index.ts",
"./constants": "./src/common/constants.ts",
"./test-utils/factories": "./src/test-utils/factories/index.ts",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ const learningResourceTopic: Factory<LearningResourceTopic> = (
const topic: LearningResourceTopic = {
id: uniqueEnforcerId.enforce(() => faker.number.int()),
name: uniqueEnforcerWords.enforce(() => faker.lorem.words()),
channel_url: `${faker.internet.url()}${faker.system.directoryPath()}`,
channel_url: `${faker.internet.url({ appendSlash: false })}${faker.system.directoryPath()}`,
parent: null,
...overrides,
}
Expand Down
2 changes: 2 additions & 0 deletions frontends/jest-shared-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ expect.extend(matchers)
// env vars
process.env.NEXT_PUBLIC_MITOL_API_BASE_URL =
"http://api.test.learn.odl.local:8063"
process.env.NEXT_PUBLIC_ORIGIN = "http://test.learn.odl.local:8062"
process.env.NEXT_PUBLIC_EMBEDLY_KEY = "fake-embedly-key"

// Pulled from the docs - see https://jestjs.io/docs/manual-mocks#mocking-methods-which-are-not-implemented-in-jsdom

Expand Down
17 changes: 17 additions & 0 deletions frontends/main/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import path from "path"
import type { Config } from "@jest/types"
import baseConfig from "../jest.jsdom.config"

const config: Config.InitialOptions = {
...baseConfig,
setupFilesAfterEnv: [
...baseConfig.setupFilesAfterEnv,
"./test-utils/setupJest.ts",
],
moduleNameMapper: {
"^@/(.*)$": path.resolve(__dirname, "src/$1"),
...baseConfig.moduleNameMapper,
},
}

export default config
18 changes: 4 additions & 14 deletions frontends/main/src/app-pages/AboutPage/AboutPage.test.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,10 @@
import { renderTestApp, screen, waitFor, setMockResponse } from "@/test-utils"
import { urls } from "api/test-utils"
import * as commonUrls from "@/common/urls"
import { Permissions } from "@/common/permissions"
import React from "react"
import { screen, renderWithProviders } from "@/test-utils"
import { AboutPage } from "./AboutPage"

describe("AboutPage", () => {
test("Renders title", async () => {
setMockResponse.get(urls.userMe.get(), {
[Permissions.Authenticated]: true,
})

renderTestApp({
url: commonUrls.ABOUT,
})
await waitFor(() => {
expect(document.title).toBe("About Us | MIT Learn")
})
Comment on lines -15 to -17
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed this and some other metadata tests since the metadata is now handled by NextJS.

It probably is worth having metadata tests, but they should be added in https://github.com/mitodl/hq/issues/5410

renderWithProviders(<AboutPage />)
screen.getByRole("heading", {
name: "About Us",
})
Expand Down
114 changes: 41 additions & 73 deletions frontends/main/src/app-pages/ChannelPage/ChannelPage.test.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import React from "react"
import { urls, factories, makeRequest } from "api/test-utils"
import { ChannelTypeEnum, type Channel } from "api/v0"
import type { LearningResourcesSearchResponse } from "api"
import {
renderTestApp,
screen,
setMockResponse,
within,
waitFor,
assertPartialMetas,
renderWithProviders,
} from "@/test-utils"
import ChannelSearch from "./ChannelSearch"
import { assertHeadings } from "ol-test-utilities"
import { assertHeadings, getByImageSrc } from "ol-test-utilities"
import invariant from "tiny-invariant"
import ChannelPage from "./ChannelPage"

jest.mock("./ChannelSearch", () => {
const actual = jest.requireActual("./ChannelSearch")
Expand Down Expand Up @@ -141,7 +141,9 @@ describe.each(ALL_CHANNEL_TYPES)(
"platform=ocw&platform=mitxonline&department=8&department=9",
channel_type: channelType,
})
renderTestApp({ url: `/c/${channel.channel_type}/${channel.name}` })
renderWithProviders(<ChannelPage />, {
url: `/c/${channel.channel_type}/${channel.name}`,
})
await screen.findAllByText(channel.title)
const expectedProps = expect.objectContaining({
constantSearchParams: {
Expand All @@ -161,7 +163,9 @@ describe.each(ALL_CHANNEL_TYPES)(
channel_type: channelType,
})
channel.search_filter = undefined
renderTestApp({ url: `/c/${channel.channel_type}/${channel.name}` })
renderWithProviders(<ChannelPage />, {
url: `/c/${channel.channel_type}/${channel.name}`,
})
await screen.findAllByText(channel.title)

expect(mockedChannelSearch).toHaveBeenCalledTimes(0)
Expand All @@ -172,7 +176,9 @@ describe.each(ALL_CHANNEL_TYPES)(
channel_type: channelType,
})
channel.search_filter = undefined
renderTestApp({ url: `/c/${channel.channel_type}/${channel.name}` })
renderWithProviders(<ChannelPage />, {
url: `/c/${channel.channel_type}/${channel.name}`,
})
await screen.findAllByText(channel.title)

await waitFor(() => {
Expand All @@ -195,7 +201,9 @@ describe.each(ALL_CHANNEL_TYPES)(
{},
{ isSubscribed },
)
renderTestApp({ url: `/c/${channel.channel_type}/${channel.name}` })
renderWithProviders(<ChannelPage />, {
url: `/c/${channel.channel_type}/${channel.name}`,
})
const subscribedButton = await screen.findByText("Follow")
expect(subscribedButton).toBeVisible()
},
Expand All @@ -212,7 +220,9 @@ describe.each(NON_UNIT_CHANNEL_TYPES)(
channel_type: channelType,
})

renderTestApp({ url: `/c/${channel.channel_type}/${channel.name}` })
renderWithProviders(<ChannelPage />, {
url: `/c/${channel.channel_type}/${channel.name}`,
})
await screen.findAllByText(channel.title)
const carousels = screen.queryByText("Featured Courses")
expect(carousels).toBe(null)
Expand All @@ -224,13 +234,10 @@ describe.each(NON_UNIT_CHANNEL_TYPES)(
channel_type: channelType,
})

renderTestApp({ url: `/c/${channel.channel_type}/${channel.name}` })
const title = await screen.findByRole("heading", { name: channel.title })
await waitFor(() => {
assertPartialMetas({
title: `${channel.title} | ${APP_SETTINGS.SITE_NAME}`,
})
const { view } = renderWithProviders(<ChannelPage />, {
url: `/c/${channel.channel_type}/${channel.name}`,
})
const title = await screen.findByRole("heading", { name: channel.title })
// Banner background image
expect(
someAncestor(title, (el) =>
Expand All @@ -240,19 +247,20 @@ describe.each(NON_UNIT_CHANNEL_TYPES)(
),
).toBe(true)
// logo
const images = screen.getAllByRole<HTMLImageElement>("img")
const logos = images.filter((img) =>
img.src.includes(channel.configuration.logo),
getByImageSrc(
view.container,
`${window.origin}${channel.configuration.logo}`,
)
expect(logos.length).toBe(1)
})

test("headings", async () => {
const { channel } = setupApis({
search_filter: "topic=Physics",
channel_type: channelType,
})
renderTestApp({ url: `/c/${channel.channel_type}/${channel.name}` })
renderWithProviders(<ChannelPage />, {
url: `/c/${channel.channel_type}/${channel.name}`,
})

await waitFor(() => {
assertHeadings([
Expand All @@ -272,7 +280,9 @@ describe("Channel Pages, Topic only", () => {
search_filter: "topic=Physics",
channel_type: ChannelTypeEnum.Topic,
})
renderTestApp({ url: `/c/${channel.channel_type}/${channel.name}` })
renderWithProviders(<ChannelPage />, {
url: `/c/${channel.channel_type}/${channel.name}`,
})

invariant(subTopics)
const links = await screen.findAllByRole("link", {
Expand All @@ -286,71 +296,27 @@ describe("Channel Pages, Topic only", () => {
})

describe("Channel Pages, Unit only", () => {
it("Sets the expected meta tags", async () => {
const { channel } = setupApis({
search_filter: "offered_by=ocw",
channel_type: "unit",
})
renderTestApp({ url: `/c/${channel.channel_type}/${channel.name}` })
const title = `${channel.title} | ${APP_SETTINGS.SITE_NAME}`
const { heading: description } = channel.configuration
await waitFor(() => {
assertPartialMetas({
title,
description,
og: { title, description },
})
})
})

it("Sets the expected metadata tags when resource drawer is open", async () => {
/**
* Check that the meta tags are correct on channel page, even when the
* resource drawer is open.
*/
const { channel } = setupApis({
search_filter: "offered_by=ocw",
channel_type: "unit",
})
const resource = factories.learningResources.resource()
setMockResponse.get(
urls.learningResources.details({ id: resource.id }),
resource,
)

renderTestApp({
url: `/c/${channel.channel_type}/${channel.name}?resource=${resource.id}`,
})
await screen.findByRole("heading", { name: channel.title, hidden: true })
const title = `${resource.title} | ${APP_SETTINGS.SITE_NAME}`
const description = resource.description
await waitFor(() => {
assertPartialMetas({
title,
description,
og: { title, description },
})
})
})

it("Displays the channel title, banner, and avatar", async () => {
const { channel } = setupApis({
search_filter: "offered_by=ocw",
channel_type: "unit",
})
renderTestApp({ url: `/c/${channel.channel_type}/${channel.name}` })
renderWithProviders(<ChannelPage />, {
url: `/c/${channel.channel_type}/${channel.name}`,
})

const title = await screen.findByRole("heading", { name: channel.title })
const image = within(title).getByRole<HTMLImageElement>("img")
expect(image.src).toContain(channel.configuration.logo)
getByImageSrc(title, `${window.origin}${channel.configuration.logo}`)
})
it("Displays a featured carousel if the channel type is 'unit'", async () => {
const { channel } = setupApis({
search_filter: "offered_by=ocw",
channel_type: "unit",
})

renderTestApp({ url: `/c/${channel.channel_type}/${channel.name}` })
renderWithProviders(<ChannelPage />, {
url: `/c/${channel.channel_type}/${channel.name}`,
})
await screen.findAllByText(channel.title)
const carousel = await screen.findByText("Featured Courses")
expect(carousel).toBeInTheDocument()
Expand All @@ -377,7 +343,9 @@ describe("Channel Pages, Unit only", () => {
search_filter: "offered_by=ocw",
channel_type: "unit",
})
renderTestApp({ url: `/c/${channel.channel_type}/${channel.name}` })
renderWithProviders(<ChannelPage />, {
url: `/c/${channel.channel_type}/${channel.name}`,
})

await waitFor(() => {
assertHeadings([
Expand Down
Loading