@openneuro/app 4.29.9 → 4.30.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/.scss-lint.yml +11 -11
  2. package/maintenance.html +26 -20
  3. package/package.json +3 -3
  4. package/src/@types/custom.d.ts +2 -4
  5. package/src/index.html +14 -10
  6. package/src/scripts/datalad/routes/dataset-redirect.tsx +2 -0
  7. package/src/scripts/dataset/mutations/__tests__/update-permissions.spec.jsx +1 -1
  8. package/src/scripts/dataset/mutations/update-permissions.tsx +1 -1
  9. package/src/scripts/routes.tsx +16 -3
  10. package/src/scripts/users/__tests__/user-account-view.spec.tsx +146 -63
  11. package/src/scripts/users/__tests__/user-card.spec.tsx +62 -47
  12. package/src/scripts/users/__tests__/user-query.spec.tsx +65 -60
  13. package/src/scripts/users/__tests__/user-routes.spec.tsx +71 -40
  14. package/src/scripts/users/__tests__/user-tabs.spec.tsx +63 -66
  15. package/src/scripts/users/components/edit-list.tsx +53 -29
  16. package/src/scripts/users/components/edit-string.tsx +63 -22
  17. package/src/scripts/users/components/editable-content.tsx +85 -50
  18. package/src/scripts/users/user-account-view.tsx +101 -21
  19. package/src/scripts/users/user-card.tsx +26 -24
  20. package/src/scripts/users/user-container.tsx +39 -38
  21. package/src/scripts/users/user-datasets-view.tsx +22 -22
  22. package/src/scripts/users/user-notifications-view.tsx +9 -10
  23. package/src/scripts/users/user-query.tsx +62 -66
  24. package/src/scripts/users/user-routes.tsx +31 -24
  25. package/src/scripts/users/user-tabs.tsx +25 -21
  26. package/src/scripts/utils/__tests__/markdown.spec.tsx +1 -2
  27. package/src/scripts/utils/validationUtils.ts +2 -3
  28. package/src/scripts/validation/validation.tsx +11 -8
@@ -1,7 +1,7 @@
1
- import React from "react";
2
- import { render, screen } from "@testing-library/react";
3
- import type { User } from "../user-card";
4
- import { UserCard } from "../user-card";
1
+ import React from "react"
2
+ import { render, screen } from "@testing-library/react"
3
+ import type { User } from "../user-card"
4
+ import { UserCard } from "../user-card"
5
5
 
6
6
  describe("UserCard Component", () => {
7
7
  const baseUser: User = {
@@ -12,31 +12,35 @@ describe("UserCard Component", () => {
12
12
  institution: "University of California",
13
13
  links: ["https://example.com", "https://example.org"],
14
14
  github: "johndoe",
15
- };
15
+ }
16
16
 
17
17
  it("renders all user details when all data is provided", () => {
18
-
19
- render(<UserCard user={baseUser} />);
18
+ render(<UserCard user={baseUser} />)
20
19
 
21
20
  const orcidLink = screen.getByRole("link", {
22
21
  name: "ORCID profile of John Doe",
23
- });
24
- expect(orcidLink).toHaveAttribute("href", "https://orcid.org/0000-0001-2345-6789");
25
- expect(screen.getByText("University of California")).toBeInTheDocument();
26
- expect(screen.getByText("San Francisco, CA")).toBeInTheDocument();
22
+ })
23
+ expect(orcidLink).toHaveAttribute(
24
+ "href",
25
+ "https://orcid.org/0000-0001-2345-6789",
26
+ )
27
+ expect(screen.getByText("University of California")).toBeInTheDocument()
28
+ expect(screen.getByText("San Francisco, CA")).toBeInTheDocument()
27
29
 
28
- const emailLink = screen.getByRole("link", { name: "johndoe@example.com" });
29
- expect(emailLink).toHaveAttribute("href", "mailto:johndoe@example.com");
30
+ const emailLink = screen.getByRole("link", { name: "johndoe@example.com" })
31
+ expect(emailLink).toHaveAttribute("href", "mailto:johndoe@example.com")
30
32
 
31
- const githubLink = screen.getByRole("link", { name: "Github profile of John Doe", });
32
- expect(githubLink).toHaveAttribute("href", "https://github.com/johndoe");
33
+ const githubLink = screen.getByRole("link", {
34
+ name: "Github profile of John Doe",
35
+ })
36
+ expect(githubLink).toHaveAttribute("href", "https://github.com/johndoe")
33
37
  expect(
34
- screen.getByRole("link", { name: "https://example.com" })
35
- ).toHaveAttribute("href", "https://example.com");
38
+ screen.getByRole("link", { name: "https://example.com" }),
39
+ ).toHaveAttribute("href", "https://example.com")
36
40
  expect(
37
- screen.getByRole("link", { name: "https://example.org" })
38
- ).toHaveAttribute("href", "https://example.org");
39
- });
41
+ screen.getByRole("link", { name: "https://example.org" }),
42
+ ).toHaveAttribute("href", "https://example.org")
43
+ })
40
44
 
41
45
  it("renders without optional fields", () => {
42
46
  const minimalUser: User = {
@@ -44,32 +48,39 @@ describe("UserCard Component", () => {
44
48
  email: "janedoe@example.com",
45
49
  orcid: "0000-0002-3456-7890",
46
50
  links: [],
47
- };
51
+ }
48
52
 
49
- render(<UserCard user={minimalUser} />);
53
+ render(<UserCard user={minimalUser} />)
50
54
 
51
55
  const orcidLink = screen.getByRole("link", {
52
56
  name: "ORCID profile of Jane Doe",
53
- });
54
- expect(orcidLink).toHaveAttribute("href", "https://orcid.org/0000-0002-3456-7890");
55
- const emailLink = screen.getByRole("link", { name: "janedoe@example.com" });
56
- expect(emailLink).toHaveAttribute("href", "mailto:janedoe@example.com");
57
- expect(screen.queryByText("University of California")).not.toBeInTheDocument();
58
- expect(screen.queryByText("San Francisco, CA")).not.toBeInTheDocument();
59
- expect(screen.queryByRole("link", { name: "Github profile of Jane Doe" })).not.toBeInTheDocument();
60
- });
57
+ })
58
+ expect(orcidLink).toHaveAttribute(
59
+ "href",
60
+ "https://orcid.org/0000-0002-3456-7890",
61
+ )
62
+ const emailLink = screen.getByRole("link", { name: "janedoe@example.com" })
63
+ expect(emailLink).toHaveAttribute("href", "mailto:janedoe@example.com")
64
+ expect(screen.queryByText("University of California")).not
65
+ .toBeInTheDocument()
66
+ expect(screen.queryByText("San Francisco, CA")).not.toBeInTheDocument()
67
+ expect(screen.queryByRole("link", { name: "Github profile of Jane Doe" }))
68
+ .not.toBeInTheDocument()
69
+ })
61
70
 
62
71
  it("renders correctly when links are empty", () => {
63
72
  const userWithEmptyLinks: User = {
64
73
  ...baseUser,
65
74
  links: [],
66
- };
75
+ }
67
76
 
68
- render(<UserCard user={userWithEmptyLinks} />);
77
+ render(<UserCard user={userWithEmptyLinks} />)
69
78
 
70
- expect(screen.queryByRole("link", { name: "https://example.com" })).not.toBeInTheDocument();
71
- expect(screen.queryByRole("link", { name: "https://example.org" })).not.toBeInTheDocument();
72
- });
79
+ expect(screen.queryByRole("link", { name: "https://example.com" })).not
80
+ .toBeInTheDocument()
81
+ expect(screen.queryByRole("link", { name: "https://example.org" })).not
82
+ .toBeInTheDocument()
83
+ })
73
84
 
74
85
  it("renders correctly when location and institution are missing", () => {
75
86
  const userWithoutLocationAndInstitution: User = {
@@ -77,19 +88,23 @@ describe("UserCard Component", () => {
77
88
  email: "emilydoe@example.com",
78
89
  orcid: "0000-0003-4567-8901",
79
90
  links: ["https://example.com"],
80
- };
91
+ }
81
92
 
82
- render(<UserCard user={userWithoutLocationAndInstitution} />);
93
+ render(<UserCard user={userWithoutLocationAndInstitution} />)
83
94
 
84
95
  const orcidLink = screen.getByRole("link", {
85
96
  name: "ORCID profile of Emily Doe",
86
- });
87
- expect(orcidLink).toHaveAttribute("href", "https://orcid.org/0000-0003-4567-8901");
88
- const emailLink = screen.getByRole("link", { name: "emilydoe@example.com" });
89
- expect(emailLink).toHaveAttribute("href", "mailto:emilydoe@example.com");
90
- const link = screen.getByRole("link", { name: "https://example.com" });
91
- expect(link).toHaveAttribute("href", "https://example.com");
92
- expect(screen.queryByText("San Francisco, CA")).not.toBeInTheDocument();
93
- expect(screen.queryByText("University of California")).not.toBeInTheDocument();
94
- });
95
- });
97
+ })
98
+ expect(orcidLink).toHaveAttribute(
99
+ "href",
100
+ "https://orcid.org/0000-0003-4567-8901",
101
+ )
102
+ const emailLink = screen.getByRole("link", { name: "emilydoe@example.com" })
103
+ expect(emailLink).toHaveAttribute("href", "mailto:emilydoe@example.com")
104
+ const link = screen.getByRole("link", { name: "https://example.com" })
105
+ expect(link).toHaveAttribute("href", "https://example.com")
106
+ expect(screen.queryByText("San Francisco, CA")).not.toBeInTheDocument()
107
+ expect(screen.queryByText("University of California")).not
108
+ .toBeInTheDocument()
109
+ })
110
+ })
@@ -1,60 +1,65 @@
1
- import React from "react";
2
- import { render, screen } from "@testing-library/react";
3
- import { MemoryRouter, Route, Routes } from "react-router-dom";
4
- import { UserQuery } from "../user-query";
5
- import FourOFourPage from "../../errors/404page";
6
-
7
-
8
- // TODO update these once the correct query is in place and dummy data is not used.
9
- // maybe there is a better way to do this
10
- const VALID_ORCID = "0000-0001-6755-0259";
11
- const INVALID_ORCID = "0000-000X-1234-5678";
12
- const UNKNOWN_ORCID = "0000-0000-0000-0000";
13
-
14
- const renderWithRouter = (orcid: string) => {
15
- return render(
16
- <MemoryRouter initialEntries={[`/user/${orcid}`]}>
17
- <Routes>
18
- <Route path="/user/:orcid" element={<UserQuery />} />
19
- <Route path="*" element={<FourOFourPage />} />
20
- </Routes>
21
- </MemoryRouter>
22
- );
23
- };
24
-
25
- describe("UserQuery Component", () => {
26
- // TODO update these once the correct query is in place and dummy data is not used.
27
- // maybe there is a better way to do this
28
- it("renders UserRoutes for a valid ORCID", async () => {
29
- renderWithRouter(VALID_ORCID);
30
- const userName = await screen.findByText("Gregory Noack");
31
- expect(userName).toBeInTheDocument();
32
-
33
- const userLocation = screen.getByText("Stanford, CA");
34
- expect(userLocation).toBeInTheDocument();
35
- });
36
-
37
- it("renders FourOFourPage for an invalid ORCID", async () => {
38
- renderWithRouter(INVALID_ORCID);
39
- const errorMessage = await screen.findByText(
40
- /404: The page you are looking for does not exist./i
41
- );
42
- expect(errorMessage).toBeInTheDocument();
43
- });
44
-
45
- it("renders FourOFourPage for a missing ORCID", async () => {
46
- renderWithRouter("");
47
- const errorMessage = await screen.findByText(
48
- /404: The page you are looking for does not exist./i
49
- );
50
- expect(errorMessage).toBeInTheDocument();
51
- });
52
-
53
- it("renders FourOFourPage for an unknown ORCID", async () => {
54
- renderWithRouter(UNKNOWN_ORCID);
55
- const errorMessage = await screen.findByText(
56
- /404: The page you are looking for does not exist./i
57
- );
58
- expect(errorMessage).toBeInTheDocument();
59
- });
60
- });
1
+ import React from "react"
2
+ import { render, screen, waitFor } from "@testing-library/react"
3
+ import { MockedProvider } from "@apollo/client/testing"
4
+ import { MemoryRouter, Route, Routes } from "react-router-dom"
5
+ import { UserQuery } from "../user-query"
6
+ import { GET_USER_BY_ORCID } from "../user-query"
7
+
8
+ const validOrcid = "0009-0001-9689-7232"
9
+
10
+ const userMock = {
11
+ request: {
12
+ query: GET_USER_BY_ORCID,
13
+ variables: { userId: validOrcid },
14
+ },
15
+ result: {
16
+ data: {
17
+ user: {
18
+ __typename: "User",
19
+ id: "1",
20
+ name: "Test User",
21
+ orcid: validOrcid,
22
+ email: "test@example.com",
23
+ avatar: "http://example.com/avatar.png",
24
+ },
25
+ },
26
+ },
27
+ }
28
+
29
+ const mocks = [userMock]
30
+
31
+ describe("UserQuery component", () => {
32
+ it("displays the ORCID on the page for a valid ORCID", async () => {
33
+ render(
34
+ <MockedProvider mocks={mocks} addTypename={true}>
35
+ <MemoryRouter initialEntries={[`/user/${validOrcid}`]}>
36
+ <Routes>
37
+ <Route path="/user/:orcid" element={<UserQuery />} />
38
+ </Routes>
39
+ </MemoryRouter>
40
+ </MockedProvider>,
41
+ )
42
+
43
+ await waitFor(() =>
44
+ expect(screen.queryByText("Loading...")).not.toBeInTheDocument()
45
+ )
46
+
47
+ expect(screen.getByText(validOrcid)).toBeInTheDocument()
48
+ })
49
+
50
+ it("shows 404 page for invalid ORCID", async () => {
51
+ render(
52
+ <MockedProvider mocks={[]} addTypename={true}>
53
+ <MemoryRouter initialEntries={[`/user/invalid-orcid`]}>
54
+ <Routes>
55
+ <Route path="/user/:orcid" element={<UserQuery />} />
56
+ </Routes>
57
+ </MemoryRouter>
58
+ </MockedProvider>,
59
+ )
60
+
61
+ await waitFor(() => screen.getByText(/404/i))
62
+
63
+ expect(screen.getByText(/404/i)).toBeInTheDocument()
64
+ })
65
+ })
@@ -1,11 +1,13 @@
1
- import React from "react";
2
- import { render, screen, cleanup } from "@testing-library/react";
3
- import { MemoryRouter } from "react-router-dom";
4
- import { UserRoutes } from "../user-routes";
5
- import type { User } from "../user-routes";
1
+ import React from "react"
2
+ import { cleanup, render, screen } from "@testing-library/react"
3
+ import { MemoryRouter } from "react-router-dom"
4
+ import { MockedProvider } from "@apollo/client/testing"
5
+ import { UserRoutes } from "../user-routes"
6
+ import type { User } from "../user-routes"
7
+ import { UPDATE_USER } from "../user-query"
6
8
 
7
9
  const defaultUser: User = {
8
- id: "1",
10
+ id: "1",
9
11
  name: "John Doe",
10
12
  location: "Unknown",
11
13
  github: "",
@@ -14,58 +16,87 @@ const defaultUser: User = {
14
16
  avatar: "https://dummyimage.com/200x200/000/fff",
15
17
  orcid: "0000-0000-0000-0000",
16
18
  links: [],
17
- };
19
+ }
20
+
21
+ const mocks = [
22
+ {
23
+ request: {
24
+ query: UPDATE_USER,
25
+ variables: {
26
+ id: "1",
27
+ name: "John Doe",
28
+ location: "Unknown",
29
+ github: "",
30
+ institution: "Unknown Institution",
31
+ email: "john.doe@example.com",
32
+ avatar: "https://dummyimage.com/200x200/000/fff",
33
+ orcid: "0000-0000-0000-0000",
34
+ links: [],
35
+ },
36
+ },
37
+ result: {
38
+ data: {
39
+ updateUser: {
40
+ id: "1",
41
+ name: "John Doe",
42
+ },
43
+ },
44
+ },
45
+ },
46
+ ]
18
47
 
19
48
  const renderWithRouter = (user: User, route: string, hasEdit: boolean) => {
20
49
  return render(
21
- <MemoryRouter initialEntries={[route]}>
22
- <UserRoutes user={user} hasEdit={hasEdit} />
23
- </MemoryRouter>
24
- );
25
- };
50
+ <MockedProvider mocks={mocks} addTypename={false}>
51
+ <MemoryRouter initialEntries={[route]}>
52
+ <UserRoutes user={user} hasEdit={hasEdit} />
53
+ </MemoryRouter>
54
+ </MockedProvider>,
55
+ )
56
+ }
26
57
 
27
58
  describe("UserRoutes Component", () => {
28
- const user: User = defaultUser;
59
+ const user: User = defaultUser
29
60
 
30
61
  it("renders UserDatasetsView for the default route", async () => {
31
- renderWithRouter(user, "/", true);
32
- expect(screen.getByText(`${user.name}'s Datasets`)).toBeInTheDocument();
33
- const datasetsView = await screen.findByTestId("user-datasets-view");
34
- expect(datasetsView).toBeInTheDocument();
35
- });
62
+ renderWithRouter(user, "/", true)
63
+ expect(screen.getByText(`${user.name}'s Datasets`)).toBeInTheDocument()
64
+ const datasetsView = await screen.findByTestId("user-datasets-view")
65
+ expect(datasetsView).toBeInTheDocument()
66
+ })
36
67
 
37
68
  it("renders FourOFourPage for an invalid route", async () => {
38
- renderWithRouter(user, "/nonexistent-route", true);
69
+ renderWithRouter(user, "/nonexistent-route", true)
39
70
  const errorMessage = await screen.findByText(
40
- /404: The page you are looking for does not exist./i
41
- );
42
- expect(errorMessage).toBeInTheDocument();
43
- });
71
+ /404: The page you are looking for does not exist./i,
72
+ )
73
+ expect(errorMessage).toBeInTheDocument()
74
+ })
44
75
 
45
76
  it("renders UserAccountView when hasEdit is true", async () => {
46
- renderWithRouter(user, "/account", true);
47
- const accountView = await screen.findByTestId("user-account-view");
48
- expect(accountView).toBeInTheDocument();
49
- });
77
+ renderWithRouter(user, "/account", true)
78
+ const accountView = await screen.findByTestId("user-account-view")
79
+ expect(accountView).toBeInTheDocument()
80
+ })
50
81
 
51
82
  it("renders UserNotificationsView when hasEdit is true", async () => {
52
- renderWithRouter(user, "/notifications", true);
83
+ renderWithRouter(user, "/notifications", true)
53
84
  const notificationsView = await screen.findByTestId(
54
- "user-notifications-view"
55
- );
56
- expect(notificationsView).toBeInTheDocument();
57
- });
85
+ "user-notifications-view",
86
+ )
87
+ expect(notificationsView).toBeInTheDocument()
88
+ })
58
89
 
59
90
  it("renders FourOThreePage when hasEdit is false for restricted routes", async () => {
60
- const restrictedRoutes = ["/account", "/notifications"];
91
+ const restrictedRoutes = ["/account", "/notifications"]
61
92
 
62
93
  for (const route of restrictedRoutes) {
63
- cleanup();
64
- renderWithRouter(user, route, false);
94
+ cleanup()
95
+ renderWithRouter(user, route, false)
65
96
  const errorMessage = await screen.findByText(
66
- /403: You do not have access to this page, you may need to sign in./i
67
- );
68
- expect(errorMessage).toBeInTheDocument();
97
+ /403: You do not have access to this page, you may need to sign in./i,
98
+ )
99
+ expect(errorMessage).toBeInTheDocument()
69
100
  }
70
- });
71
- });
101
+ })
102
+ })
@@ -1,11 +1,11 @@
1
- import React, { useState } from "react";
2
- import { render, screen, fireEvent } from "@testing-library/react";
3
- import { MemoryRouter, Route, Routes } from "react-router-dom";
4
- import { UserAccountTabs } from "../user-tabs";
1
+ import React, { useState } from "react"
2
+ import { fireEvent, render, screen } from "@testing-library/react"
3
+ import { MemoryRouter, Route, Routes } from "react-router-dom"
4
+ import { UserAccountTabs } from "../user-tabs"
5
5
 
6
6
  // Wrapper component to allow dynamic modification of `hasEdit`
7
7
  const UserAccountTabsWrapper: React.FC = () => {
8
- const [hasEdit, setHasEdit] = useState(true);
8
+ const [hasEdit, setHasEdit] = useState(true)
9
9
 
10
10
  return (
11
11
  <>
@@ -16,72 +16,69 @@ const UserAccountTabsWrapper: React.FC = () => {
16
16
  </Routes>
17
17
  </MemoryRouter>
18
18
  </>
19
- );
20
- };
19
+ )
20
+ }
21
21
 
22
22
  describe("UserAccountTabs Component", () => {
23
23
  it("should not render tabs when hasEdit is false", () => {
24
- render(<UserAccountTabsWrapper />);
24
+ render(<UserAccountTabsWrapper />)
25
25
 
26
- expect(screen.getByText("User Datasets")).toBeInTheDocument();
26
+ expect(screen.getByText("User Datasets")).toBeInTheDocument()
27
27
 
28
- fireEvent.click(screen.getByText("Toggle hasEdit"));
28
+ fireEvent.click(screen.getByText("Toggle hasEdit"))
29
29
 
30
- expect(screen.queryByText("User Datasets")).not.toBeInTheDocument();
31
- });
30
+ expect(screen.queryByText("User Datasets")).not.toBeInTheDocument()
31
+ })
32
32
 
33
33
  it("should render tabs when hasEdit is toggled back to true", () => {
34
- render(<UserAccountTabsWrapper />);
35
-
36
- expect(screen.getByText("User Datasets")).toBeInTheDocument();
37
-
38
- fireEvent.click(screen.getByText("Toggle hasEdit"));
39
- expect(screen.queryByText("User Datasets")).not.toBeInTheDocument();
40
-
41
- fireEvent.click(screen.getByText("Toggle hasEdit"));
42
- expect(screen.getByText("User Datasets")).toBeInTheDocument();
43
- });
44
-
45
- it("should update active class on the correct NavLink based on route", () => {
46
- render(<UserAccountTabsWrapper />);
47
-
48
- // Utility function to check if an element has 'active' class - used because of CSS module discrepancies between classNames
49
- const hasActiveClass = (element) => element.className.includes('active');
50
-
51
- const datasetsTab = screen.getByText("User Datasets");
52
- expect(hasActiveClass(datasetsTab)).toBe(true);
53
-
54
- const notificationsTab = screen.getByText("User Notifications");
55
-
56
- fireEvent.click(notificationsTab);
57
-
58
- expect(hasActiveClass(notificationsTab)).toBe(true);
59
- expect(hasActiveClass(datasetsTab)).toBe(false);
60
-
61
- const accountTab = screen.getByText("Account Info");
62
-
63
- fireEvent.click(accountTab);
64
-
65
- expect(hasActiveClass(accountTab)).toBe(true);
66
- expect(hasActiveClass(datasetsTab)).toBe(false);
67
- expect(hasActiveClass(notificationsTab)).toBe(false);
68
- });
69
-
70
-
71
- it("should trigger animation state when a tab is clicked", async () => {
72
- render(<UserAccountTabsWrapper />);
73
-
74
- const notificationsTab = screen.getByText("User Notifications");
75
- // Utility function to check if an element has 'clicked' class - used because of CSS module discrepancies between classNames
76
- const hasClickedClass = (element) => element.className.includes('clicked');
77
- const tabsContainer = await screen.findByRole("list");
78
-
79
- expect(hasClickedClass(tabsContainer)).toBe(false);
80
-
81
- fireEvent.click(notificationsTab);
82
-
83
- expect(hasClickedClass(tabsContainer)).toBe(true);
84
- });
85
-
86
-
87
- });
34
+ render(<UserAccountTabsWrapper />)
35
+
36
+ expect(screen.getByText("User Datasets")).toBeInTheDocument()
37
+
38
+ fireEvent.click(screen.getByText("Toggle hasEdit"))
39
+ expect(screen.queryByText("User Datasets")).not.toBeInTheDocument()
40
+
41
+ fireEvent.click(screen.getByText("Toggle hasEdit"))
42
+ expect(screen.getByText("User Datasets")).toBeInTheDocument()
43
+ })
44
+
45
+ it("should update active class on the correct NavLink based on route", () => {
46
+ render(<UserAccountTabsWrapper />)
47
+
48
+ // Utility function to check if an element has 'active' class - used because of CSS module discrepancies between classNames
49
+ const hasActiveClass = (element) => element.className.includes("active")
50
+
51
+ const datasetsTab = screen.getByText("User Datasets")
52
+ expect(hasActiveClass(datasetsTab)).toBe(true)
53
+
54
+ const notificationsTab = screen.getByText("User Notifications")
55
+
56
+ fireEvent.click(notificationsTab)
57
+
58
+ expect(hasActiveClass(notificationsTab)).toBe(true)
59
+ expect(hasActiveClass(datasetsTab)).toBe(false)
60
+
61
+ const accountTab = screen.getByText("Account Info")
62
+
63
+ fireEvent.click(accountTab)
64
+
65
+ expect(hasActiveClass(accountTab)).toBe(true)
66
+ expect(hasActiveClass(datasetsTab)).toBe(false)
67
+ expect(hasActiveClass(notificationsTab)).toBe(false)
68
+ })
69
+
70
+ it("should trigger animation state when a tab is clicked", async () => {
71
+ render(<UserAccountTabsWrapper />)
72
+
73
+ const notificationsTab = screen.getByText("User Notifications")
74
+ // Utility function to check if an element has 'clicked' class - used because of CSS module discrepancies between classNames
75
+ const hasClickedClass = (element) => element.className.includes("clicked")
76
+ const tabsContainer = await screen.findByRole("list")
77
+
78
+ expect(hasClickedClass(tabsContainer)).toBe(false)
79
+
80
+ fireEvent.click(notificationsTab)
81
+
82
+ expect(hasClickedClass(tabsContainer)).toBe(true)
83
+ })
84
+ })