diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index dfd95fb5d..356da7c17 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -17,6 +17,42 @@ env:
CACHE_NUMBER: 1
jobs:
+ unit-test-frontend:
+ name: Frontend linting / unit tests
+ # Defines the type of runner the job runs on
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout to the repository
+ uses: actions/checkout@v3
+ - name: Set up NodeJS environment
+ uses: actions/setup-node@v3
+ with:
+ node-version: '14'
+ # Consider this as an add on to optimize the execution of actions
+ - name: Cache node modules
+ id: cache-npm
+ uses: actions/cache@v3
+ env:
+ cache-name: cache-node-modules
+ with:
+ # npm cache files are stored in `~/.npm` on Linux/macOS
+ path: ~/.npm
+ key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
+ restore-keys: |
+ ${{ runner.os }}-build-${{ env.cache-name }}-
+ ${{ runner.os }}-build-
+ ${{ runner.os }}-
+ - name: Install package dependencies
+ run: npm install
+ working-directory: ./frontend2
+ - name: Check linting and formatting
+ # Custom script for checking the linting and formatting being in place
+ run: npm run lint
+ working-directory: ./frontend2
+ # Run test cases and this could ensure minimum coverage as well if set
+ - name: Execute test cases
+ run: npm run test
+ working-directory: ./frontend2
lint:
name: Linter (pre-commit)
runs-on: ubuntu-latest
@@ -40,8 +76,8 @@ jobs:
run: |
pre-commit install
pre-commit run -a
- unit-test:
- name: Unit tests (django)
+ unit-test-backend:
+ name: Backend unit tests
runs-on: ubuntu-latest
permissions:
contents: read
diff --git a/frontend2/.eslintrc.js b/frontend2/.eslintrc.js
index b6bef5f20..4a8b759d8 100644
--- a/frontend2/.eslintrc.js
+++ b/frontend2/.eslintrc.js
@@ -10,6 +10,7 @@ module.exports = {
"plugin:react/recommended",
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
+ "plugin:@typescript-eslint/recommended-requiring-type-checking",
"prettier",
],
ignorePatterns: [
@@ -28,6 +29,7 @@ module.exports = {
plugins: ["react"],
rules: {
indent: ["error", 2],
+ semi: "error", // require semicolons ending statements
},
settings: {
react: {
diff --git a/frontend2/src/components/EpisodeLayout.tsx b/frontend2/src/components/EpisodeLayout.tsx
index 41b9ba710..0086f13fe 100644
--- a/frontend2/src/components/EpisodeLayout.tsx
+++ b/frontend2/src/components/EpisodeLayout.tsx
@@ -14,7 +14,7 @@ const EpisodeLayout: React.FC = () => {
}
return (
-
+
diff --git a/frontend2/src/components/sidebar/SidebarItem.tsx b/frontend2/src/components/sidebar/SidebarItem.tsx
index 85c4e01f3..80d5d9ff2 100644
--- a/frontend2/src/components/sidebar/SidebarItem.tsx
+++ b/frontend2/src/components/sidebar/SidebarItem.tsx
@@ -7,13 +7,8 @@ interface SidebarItemProps {
linkTo: string;
}
-const SidebarItem: React.FC
= ({
- icon,
- text,
- linkTo,
-}) => {
- const baseStyle =
- "text-base flex items-center gap-3 ";
+const SidebarItem: React.FC = ({ icon, text, linkTo }) => {
+ const baseStyle = "text-base flex items-center gap-3 ";
const colorVariants = {
gray: "text-gray-800 hover:text-gray-400",
color: "text-teal",
diff --git a/frontend2/src/components/sidebar/SidebarSection.tsx b/frontend2/src/components/sidebar/SidebarSection.tsx
index a99a91d64..cf3e61642 100644
--- a/frontend2/src/components/sidebar/SidebarSection.tsx
+++ b/frontend2/src/components/sidebar/SidebarSection.tsx
@@ -1,17 +1,19 @@
import React from "react";
interface SidebarSectionProps {
- children?: React.ReactNode,
+ children?: React.ReactNode;
title?: string;
}
const SidebarSection: React.FC = ({ children, title }) => {
return (
- {title !== undefined &&
{title}
}
-
- {children}
-
+ {title !== undefined && (
+
+ {title}
+
+ )}
+
{children}
);
};
diff --git a/frontend2/src/components/sidebar/__test__/Sidebar.test.tsx b/frontend2/src/components/sidebar/__test__/Sidebar.test.tsx
index 99ebe9f69..487590334 100644
--- a/frontend2/src/components/sidebar/__test__/Sidebar.test.tsx
+++ b/frontend2/src/components/sidebar/__test__/Sidebar.test.tsx
@@ -5,23 +5,43 @@ import { DEFAULT_EPISODE } from "../../../utils/constants";
import { EpisodeContext } from "../../../contexts/EpisodeContext";
import { MemoryRouter } from "react-router-dom";
-test('UI: should link to default episode', () => {
- render();
- const linkElement = screen.getByText('Resources').closest('a')?.getAttribute('href');
- expect(linkElement).toEqual(expect.stringContaining(`/${DEFAULT_EPISODE}/resources`));
+test("UI: should link to default episode", () => {
+ render(
+
+
+
+ );
+ const linkElement = screen
+ .getByText("Resources")
+ .closest("a")
+ ?.getAttribute("href");
+ expect(linkElement).toEqual(
+ expect.stringContaining(`/${DEFAULT_EPISODE}/resources`)
+ );
});
-test('UI: should collapse sidebar', () => {
- render();
- expect(screen.queryByText('Home')).toBeNull();
+test("UI: should collapse sidebar", () => {
+ render(
+
+
+
+ );
+ expect(screen.queryByText("Home")).toBeNull();
});
-test('UI: should link to episode in surrounding context', () => {
- render(
- undefined }}>
-
-
+test("UI: should link to episode in surrounding context", () => {
+ render(
+
+ undefined }}
+ >
+
+
+
);
- const linkElement = screen.getByText('Resources').closest('a')?.getAttribute('href');
+ const linkElement = screen
+ .getByText("Resources")
+ .closest("a")
+ ?.getAttribute("href");
expect(linkElement).toEqual(expect.stringContaining(`/something/resources`));
});
diff --git a/frontend2/src/components/sidebar/index.tsx b/frontend2/src/components/sidebar/index.tsx
index be668c857..ae25e3862 100644
--- a/frontend2/src/components/sidebar/index.tsx
+++ b/frontend2/src/components/sidebar/index.tsx
@@ -2,9 +2,15 @@ import React, { useContext } from "react";
import SidebarSection from "./SidebarSection";
import SidebarItem from "./SidebarItem";
import {
- ClipboardDocumentIcon, HomeIcon, MapIcon,
- TrophyIcon, ChartBarIcon, ClockIcon,
- UserGroupIcon, ArrowUpTrayIcon, PlayCircleIcon,
+ ClipboardDocumentIcon,
+ HomeIcon,
+ MapIcon,
+ TrophyIcon,
+ ChartBarIcon,
+ ClockIcon,
+ UserGroupIcon,
+ ArrowUpTrayIcon,
+ PlayCircleIcon,
} from "@heroicons/react/24/outline";
import { EpisodeContext } from "../../contexts/EpisodeContext";
@@ -12,72 +18,66 @@ interface SidebarProps {
collapsed?: boolean;
}
-const Sidebar: React.FC = ({
- collapsed
-}) => {
+const Sidebar: React.FC = ({ collapsed }) => {
collapsed = collapsed ?? false;
const { episodeId } = useContext(EpisodeContext);
const linkBase = `/${episodeId}/`;
- return (
- collapsed ? null : (
-
-
- }
- text="Home"
- linkTo={`${linkBase}home`}
- />
- }
- text="Quick Start"
- linkTo={`${linkBase}quickstart`}
- />
- }
- text="Resources"
- linkTo={`${linkBase}resources`}
- />
-
-
- }
- text="Tournaments"
- linkTo={`${linkBase}tournaments`}
- />
- }
- text="Rankings"
- linkTo={`${linkBase}rankings`}
- />
- }
- text="Queue"
- linkTo={`${linkBase}queue`}
- />
-
-
- }
- text="My Team"
- linkTo={`${linkBase}team`}
- />
- }
- text="Submissions"
- linkTo={`${linkBase}submission`}
- />
- }
- text="Scrimmaging"
- linkTo={`${linkBase}scrimmaging`}
- />
-
-
- )
+ return collapsed ? null : (
+
+
+ }
+ text="Home"
+ linkTo={`${linkBase}home`}
+ />
+ }
+ text="Quick Start"
+ linkTo={`${linkBase}quickstart`}
+ />
+ }
+ text="Resources"
+ linkTo={`${linkBase}resources`}
+ />
+
+
+ }
+ text="Tournaments"
+ linkTo={`${linkBase}tournaments`}
+ />
+ }
+ text="Rankings"
+ linkTo={`${linkBase}rankings`}
+ />
+ }
+ text="Queue"
+ linkTo={`${linkBase}queue`}
+ />
+
+
+ }
+ text="My Team"
+ linkTo={`${linkBase}team`}
+ />
+ }
+ text="Submissions"
+ linkTo={`${linkBase}submission`}
+ />
+ }
+ text="Scrimmaging"
+ linkTo={`${linkBase}scrimmaging`}
+ />
+
+
);
-
-
};
export default Sidebar;
diff --git a/frontend2/src/index.tsx b/frontend2/src/index.tsx
index c71eaec78..cfd778490 100644
--- a/frontend2/src/index.tsx
+++ b/frontend2/src/index.tsx
@@ -1,6 +1,5 @@
import React from "react";
import ReactDOM from "react-dom/client";
-import { BrowserRouter } from "react-router-dom";
import "./index.css";
import App from "./App";