Skip to content
Open
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
25 changes: 18 additions & 7 deletions frontends/api/src/mitxonline/hooks/baskets/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { basketQueries } from "./queries"
import { useMutation, useQueryClient } from "@tanstack/react-query"
import { basketsApi } from "../../clients"
import { useUserIsAuthenticated } from "../../../hooks/user"
import type { BasketWithProduct } from "@mitodl/mitxonline-api-axios/v2"

/**
Expand All @@ -9,6 +10,7 @@ import type { BasketWithProduct } from "@mitodl/mitxonline-api-axios/v2"
*/
const useAddToBasket = () => {
const queryClient = useQueryClient()
const isAuthenticated = useUserIsAuthenticated()
return useMutation({
mutationFn: async (productId: number): Promise<BasketWithProduct> => {
const response = await basketsApi.basketsCreateFromProductCreate({
Expand All @@ -17,10 +19,16 @@ const useAddToBasket = () => {
return response.data
},
onSuccess: async () => {
// Invalidate checkout query to ensure fresh data
queryClient.invalidateQueries({
queryKey: basketQueries.basketState().queryKey,
})
// Invalidate checkout query to ensure fresh data - but only once
// authenticated. basketState hits the checkout-payload endpoint, which
// creates a real order against the basket's purchaser; an anonymous
// basket has no purchaser, so this has to wait until after the
// anonymous-checkout conversion step, not fire on every cart add.
if (isAuthenticated) {
queryClient.invalidateQueries({
queryKey: basketQueries.basketState().queryKey,
})
}
},
})
}
Expand All @@ -30,14 +38,17 @@ const useAddToBasket = () => {
*/
const useClearBasket = () => {
const queryClient = useQueryClient()
const isAuthenticated = useUserIsAuthenticated()
return useMutation({
mutationFn: async (): Promise<void> => {
await basketsApi.basketsClearDestroy()
},
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: basketQueries.basketState().queryKey,
})
if (isAuthenticated) {
queryClient.invalidateQueries({
queryKey: basketQueries.basketState().queryKey,
})
}
},
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ import {
} from "@/test-utils"
import * as mitxonline from "api/mitxonline-test-utils"
import { mitxonlineLegacyUrl } from "@/common/mitxonline"
import { makeRequest } from "api/test-utils"
import {
makeRequest,
urls as learnUrls,
factories as learnFactories,
} from "api/test-utils"
import { faker } from "@faker-js/faker/locale/en"
import moment from "moment"
import { EnrolledCourseCard } from "./EnrolledCourseCard"
Expand All @@ -20,6 +24,17 @@ const EnrollmentMode = {
Verified: "verified",
} as const

// useAddToBasket/useClearBasket check Learn's own auth state (separate from
// mitxonline's) before invalidating the checkout-payload query. File-scoped
// (not per-describe) since several describe blocks below each have their own
// tests that don't all route through setupUserApis.
beforeEach(() => {
setMockResponse.get(
learnUrls.userMe.get(),
learnFactories.user.user({ is_authenticated: true }),
)
})

const setupUserApis = (
overrides?: Parameters<typeof mitxonline.factories.user.user>[0],
) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
} from "@/test-utils"
import { HomeEnrollmentsDisplay } from "./HomeEnrollmentsDisplay"
import * as mitxonline from "api/mitxonline-test-utils"
import { urls as learnUrls, factories as learnFactories } from "api/test-utils"
import { useFeatureFlagEnabled } from "posthog-js/react"
import { setupEnrollments } from "./test-utils"
import { faker } from "@faker-js/faker/locale/en"
Expand All @@ -38,6 +39,17 @@ const mockedUseFeatureFlagEnabled = jest
describe("HomeEnrollmentsDisplay", () => {
setupLocationMock()

// useAddToBasket/useClearBasket check Learn's own auth state (separate
// from mitxonline's) before invalidating the checkout-payload query. Set at
// the describe level since many tests here mock the rest of the user/
// enrollment APIs inline rather than through setupApis below.
beforeEach(() => {
setMockResponse.get(
learnUrls.userMe.get(),
learnFactories.user.user({ is_authenticated: true }),
)
})

const setupApis = (includeExpired: boolean = true) => {
const mitxOnlineUser = mitxonline.factories.user.user()
setMockResponse.get(mitxonline.urls.userMe.get(), mitxOnlineUser)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ describe("OrganizationCards", () => {
description: "Test Description",
logo: "https://example.com/logo1.png",
slug: "org-test",
sso_organization_id: null,
contracts: [
mitxOnlineFactories.contracts.contract({
id: 1,
Expand Down Expand Up @@ -116,6 +117,7 @@ describe("OrganizationCards", () => {
description: "Test description",
logo: "https://example.com/logo.png",
slug: "org-test-org",
sso_organization_id: null,
contracts: [
mitxOnlineFactories.contracts.contract({
id: 1,
Expand Down Expand Up @@ -168,6 +170,7 @@ describe("OrganizationCards", () => {
description: "Test description",
logo: "https://example.com/logo.png",
slug: "org-test-org",
sso_organization_id: null,
contracts: [contract1, contract2],
})

Expand Down Expand Up @@ -200,6 +203,7 @@ describe("OrganizationCards", () => {
description: "Test description",
logo: "https://example.com/logo.png",
slug: "org-test-org",
sso_organization_id: null,
contracts: [
mitxOnlineFactories.contracts.contract({
id: 1,
Expand All @@ -225,6 +229,7 @@ describe("OrganizationCards", () => {
description: "Test description",
logo: "https://example.com/logo.png",
slug: "org-test-org",
sso_organization_id: null,
contracts: [], // No contracts for this organization
})

Expand Down Expand Up @@ -253,6 +258,7 @@ describe("OrganizationCards", () => {
description: "Test description",
logo: "https://example.com/logo1.png",
slug: "org-one",
sso_organization_id: null,
contracts: [
mitxOnlineFactories.contracts.contract({
id: 1,
Expand All @@ -274,6 +280,7 @@ describe("OrganizationCards", () => {
description: "Test description",
logo: "https://example.com/logo2.png",
slug: "org-two",
sso_organization_id: null,
contracts: [
mitxOnlineFactories.contracts.contract({
id: 3,
Expand Down Expand Up @@ -341,6 +348,7 @@ describe("OrganizationCards", () => {
name: "Test Organization",
logo: undefined,
slug: "org-test-org",
sso_organization_id: null,
contracts: [
mitxOnlineFactories.contracts.contract({
id: 1,
Expand Down Expand Up @@ -376,6 +384,7 @@ describe("OrganizationCards", () => {
description: "Test description",
logo: "https://example.com/logo.png",
slug: "org-my-company",
sso_organization_id: null,
contracts: [contract],
})

Expand Down Expand Up @@ -403,6 +412,7 @@ describe("OrganizationCards", () => {
description: "Test description",
logo: "https://example.com/logo.png",
slug: "my-company", // No 'org-' prefix
sso_organization_id: null,
contracts: [contract],
})

Expand All @@ -424,6 +434,7 @@ describe("OrganizationCards", () => {
description: "Test description",
logo: "https://example.com/logo.png",
slug: "org-test-org",
sso_organization_id: null,
contracts: [], // No contracts for this organization
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import {
user,
within,
} from "@/test-utils"
import { makeRequest } from "api/test-utils"
import {
makeRequest,
urls as learnUrls,
factories as learnFactories,
} from "api/test-utils"
import * as mitxonline from "api/mitxonline-test-utils"
import { ProgramAsCourseCard } from "./ProgramAsCourseCard"
import { waitFor } from "@testing-library/react"
Expand All @@ -22,6 +26,15 @@ jest.mock("posthog-js/react")
describe("ProgramAsCourseCard", () => {
setupLocationMock()

// useAddToBasket/useClearBasket check Learn's own auth state (separate
// from mitxonline's) before invalidating the checkout-payload query.
beforeEach(() => {
setMockResponse.get(
learnUrls.userMe.get(),
learnFactories.user.user({ is_authenticated: true }),
)
})

/**
* Creates a ProgramAsCourseCard data set with:
* - A program with two module courses linked via req_tree
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ import {
} from "@/test-utils"
import { ProgramEnrollmentDisplay } from "./ProgramEnrollmentDisplay"
import * as mitxonline from "api/mitxonline-test-utils"
import { makeRequest } from "api/test-utils"
import {
makeRequest,
urls as learnUrls,
factories as learnFactories,
} from "api/test-utils"
import { useFeatureFlagEnabled } from "posthog-js/react"
import { faker } from "@faker-js/faker/locale/en"
import invariant from "tiny-invariant"
Expand All @@ -39,6 +43,16 @@ const mockedUseFeatureFlagEnabled = jest

describe("ProgramEnrollmentDisplay", () => {
setupLocationMock()

// useAddToBasket/useClearBasket check Learn's own auth state (separate
// from mitxonline's) before invalidating the checkout-payload query.
beforeEach(() => {
setMockResponse.get(
learnUrls.userMe.get(),
learnFactories.user.user({ is_authenticated: true }),
)
})

test("Filters to single program when programId is provided", async () => {
const mitxOnlineUser = mitxonline.factories.user.user()
setMockResponse.get(mitxonline.urls.userMe.get(), mitxOnlineUser)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import {
within,
} from "@/test-utils"
import * as mitxonline from "api/mitxonline-test-utils"
import { makeRequest } from "api/test-utils"
import {
makeRequest,
urls as learnUrls,
factories as learnFactories,
} from "api/test-utils"
import { faker } from "@faker-js/faker/locale/en"
import moment from "moment"
import { cartesianProduct } from "ol-test-utilities"
Expand All @@ -25,6 +29,17 @@ const mitxOnlineCourse = mitxonline.factories.courses.course

const mitxUser = mitxonline.factories.user.user

// useAddToBasket/useClearBasket check Learn's own auth state (separate from
// mitxonline's) before invalidating the checkout-payload query. File-scoped
// (not per-describe) since several describe blocks below each have their own
// tests that don't all route through setupUserApis.
beforeEach(() => {
setMockResponse.get(
learnUrls.userMe.get(),
learnFactories.user.user({ is_authenticated: true }),
)
})

const setupUserApis = (overrides?: Parameters<typeof mitxUser>[0]) => {
const userData = mitxonline.factories.user.user({
is_staff: false,
Expand Down
Loading
Loading