Skip to content

Commit

Permalink
Add frontend lint / tests to ci workflow (#655)
Browse files Browse the repository at this point in the history
  • Loading branch information
acrantel committed Aug 15, 2023
1 parent da992ec commit 3e9b7ef
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 93 deletions.
40 changes: 38 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
2 changes: 2 additions & 0 deletions frontend2/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module.exports = {
"plugin:react/recommended",
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking",
"prettier",
],
ignorePatterns: [
Expand All @@ -28,6 +29,7 @@ module.exports = {
plugins: ["react"],
rules: {
indent: ["error", 2],
semi: "error", // require semicolons ending statements
},
settings: {
react: {
Expand Down
2 changes: 1 addition & 1 deletion frontend2/src/components/EpisodeLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const EpisodeLayout: React.FC = () => {
}
return (
<div className="h-screen">
<Navbar/>
<Navbar />
<div className="flex flex-row h-full">
<Sidebar />
<Outlet />
Expand Down
9 changes: 2 additions & 7 deletions frontend2/src/components/sidebar/SidebarItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,8 @@ interface SidebarItemProps {
linkTo: string;
}

const SidebarItem: React.FC<SidebarItemProps> = ({
icon,
text,
linkTo,
}) => {
const baseStyle =
"text-base flex items-center gap-3 ";
const SidebarItem: React.FC<SidebarItemProps> = ({ 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",
Expand Down
12 changes: 7 additions & 5 deletions frontend2/src/components/sidebar/SidebarSection.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import React from "react";

interface SidebarSectionProps {
children?: React.ReactNode,
children?: React.ReactNode;
title?: string;
}

const SidebarSection: React.FC<SidebarSectionProps> = ({ children, title }) => {
return (
<div className="pl-5 pr-8">
{title !== undefined && <h2 className="uppercase text-gray-500 tracking-wider text-sm mb-3">{title}</h2>}
<div className="flex flex-col gap-5">
{children}
</div>
{title !== undefined && (
<h2 className="uppercase text-gray-500 tracking-wider text-sm mb-3">
{title}
</h2>
)}
<div className="flex flex-col gap-5">{children}</div>
</div>
);
};
Expand Down
46 changes: 33 additions & 13 deletions frontend2/src/components/sidebar/__test__/Sidebar.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(<MemoryRouter><Sidebar /></MemoryRouter>);
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(
<MemoryRouter>
<Sidebar />
</MemoryRouter>
);
const linkElement = screen
.getByText("Resources")
.closest("a")
?.getAttribute("href");
expect(linkElement).toEqual(
expect.stringContaining(`/${DEFAULT_EPISODE}/resources`)
);
});

test('UI: should collapse sidebar', () => {
render(<MemoryRouter><Sidebar collapsed={true} /></MemoryRouter>);
expect(screen.queryByText('Home')).toBeNull();
test("UI: should collapse sidebar", () => {
render(
<MemoryRouter>
<Sidebar collapsed={true} />
</MemoryRouter>
);
expect(screen.queryByText("Home")).toBeNull();
});

test('UI: should link to episode in surrounding context', () => {
render(<MemoryRouter>
<EpisodeContext.Provider value={{ episodeId: "something", setEpisodeId: (_) => undefined }}>
<Sidebar />
</EpisodeContext.Provider></MemoryRouter>
test("UI: should link to episode in surrounding context", () => {
render(
<MemoryRouter>
<EpisodeContext.Provider
value={{ episodeId: "something", setEpisodeId: (_) => undefined }}
>
<Sidebar />
</EpisodeContext.Provider>
</MemoryRouter>
);
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`));
});
128 changes: 64 additions & 64 deletions frontend2/src/components/sidebar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,82 +2,82 @@ 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";

interface SidebarProps {
collapsed?: boolean;
}

const Sidebar: React.FC<SidebarProps> = ({
collapsed
}) => {
const Sidebar: React.FC<SidebarProps> = ({ collapsed }) => {
collapsed = collapsed ?? false;
const { episodeId } = useContext(EpisodeContext);
const linkBase = `/${episodeId}/`;

return (
collapsed ? null : (
<div className="flex flex-col gap-8 py-4 h-full bg-gray-50 shadow-gray-200 shadow-sm">
<SidebarSection title="" >
<SidebarItem
icon={<HomeIcon className="h-6 w-6" />}
text="Home"
linkTo={`${linkBase}home`}
/>
<SidebarItem
icon={<MapIcon className="h-6 w-6" />}
text="Quick Start"
linkTo={`${linkBase}quickstart`}
/>
<SidebarItem
icon={<ClipboardDocumentIcon className="h-6 w-6" />}
text="Resources"
linkTo={`${linkBase}resources`}
/>
</SidebarSection>
<SidebarSection title="compete" >
<SidebarItem
icon={<TrophyIcon className="h-6 w-6" />}
text="Tournaments"
linkTo={`${linkBase}tournaments`}
/>
<SidebarItem
icon={<ChartBarIcon className="h-6 w-6" />}
text="Rankings"
linkTo={`${linkBase}rankings`}
/>
<SidebarItem
icon={<ClockIcon className="h-6 w-6" />}
text="Queue"
linkTo={`${linkBase}queue`}
/>
</SidebarSection>
<SidebarSection title="team management" >
<SidebarItem
icon={<UserGroupIcon className="h-6 w-6" />}
text="My Team"
linkTo={`${linkBase}team`}
/>
<SidebarItem
icon={<ArrowUpTrayIcon className="h-6 w-6" />}
text="Submissions"
linkTo={`${linkBase}submission`}
/>
<SidebarItem
icon={<PlayCircleIcon className="h-6 w-6" />}
text="Scrimmaging"
linkTo={`${linkBase}scrimmaging`}
/>
</SidebarSection>
</div>
)
return collapsed ? null : (
<div className="flex flex-col gap-8 py-4 h-full bg-gray-50 shadow-gray-200 shadow-sm">
<SidebarSection title="">
<SidebarItem
icon={<HomeIcon className="h-6 w-6" />}
text="Home"
linkTo={`${linkBase}home`}
/>
<SidebarItem
icon={<MapIcon className="h-6 w-6" />}
text="Quick Start"
linkTo={`${linkBase}quickstart`}
/>
<SidebarItem
icon={<ClipboardDocumentIcon className="h-6 w-6" />}
text="Resources"
linkTo={`${linkBase}resources`}
/>
</SidebarSection>
<SidebarSection title="compete">
<SidebarItem
icon={<TrophyIcon className="h-6 w-6" />}
text="Tournaments"
linkTo={`${linkBase}tournaments`}
/>
<SidebarItem
icon={<ChartBarIcon className="h-6 w-6" />}
text="Rankings"
linkTo={`${linkBase}rankings`}
/>
<SidebarItem
icon={<ClockIcon className="h-6 w-6" />}
text="Queue"
linkTo={`${linkBase}queue`}
/>
</SidebarSection>
<SidebarSection title="team management">
<SidebarItem
icon={<UserGroupIcon className="h-6 w-6" />}
text="My Team"
linkTo={`${linkBase}team`}
/>
<SidebarItem
icon={<ArrowUpTrayIcon className="h-6 w-6" />}
text="Submissions"
linkTo={`${linkBase}submission`}
/>
<SidebarItem
icon={<PlayCircleIcon className="h-6 w-6" />}
text="Scrimmaging"
linkTo={`${linkBase}scrimmaging`}
/>
</SidebarSection>
</div>
);


};

export default Sidebar;
1 change: 0 additions & 1 deletion frontend2/src/index.tsx
Original file line number Diff line number Diff line change
@@ -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";

Expand Down

0 comments on commit 3e9b7ef

Please sign in to comment.