Skip to content

Commit

Permalink
Rebase - P11 (#34)
Browse files Browse the repository at this point in the history
* Some clean up, added Spotify icon for offline/loading state

* Minor clean up in Mobile view

* Updating about text.

* Fixing max width for the Tab Panel

* P-18: fixed bug when type is not track

* P-20: Add real time spotify streaming progress bar. (#21)

* P-23: Added experience section. (#24)

* P-26: Added placeholder projects section. (#27)

* Wip footer.

* develop: some footer updates, brb.

* develop: simple footer.

* P-11 (#31)

* P-11: added vitest, react testing library config for unit testing.

* P-11: add Projects unit test.

* P-11: fix bg card color.

* P-11 (#32)

* P-11: added vitest, react testing library config for unit testing.

* P-11: add Projects unit test.

* P-11: fix bg card color.

* P-11: almost done, missing Github actions and TODOS in test.

* Update src/components/Navigation/Navigation.test.tsx
  • Loading branch information
mariatorrentedev authored May 20, 2024
1 parent 67070ec commit 1225812
Show file tree
Hide file tree
Showing 28 changed files with 2,129 additions and 87 deletions.
1,898 changes: 1,869 additions & 29 deletions package-lock.json

Large diffs are not rendered by default.

14 changes: 10 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
"preview": "vite preview",
"test": "vitest",
"coverage": "vitest run --coverage"
},
"dependencies": {
"@emotion/react": "^11.11.4",
Expand All @@ -23,15 +25,19 @@
"react-query": "^3.39.3"
},
"devDependencies": {
"@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22",
"@testing-library/react": "^15.0.7",
"@types/react": "^18.3.2",
"@types/react-dom": "^18.3.0",
"@typescript-eslint/eslint-plugin": "^7.2.0",
"@typescript-eslint/parser": "^7.2.0",
"@vitejs/plugin-react": "^4.2.1",
"eslint": "^8.57.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.6",
"jsdom": "^24.0.0",
"msw": "^2.3.0",
"typescript": "^5.2.2",
"vite": "^5.2.0"
"vite": "^5.2.0",
"vitest": "^1.6.0"
}
}
3 changes: 2 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@ export default function App() {
margin="0 auto"
alignItems="center"
padding={["2rem", "5rem 2rem"]}
sx={{ ".MuiGrid-root": { paddingLeft: 0 } }}
spacing={2}
>
<Grid item>
<Typography fontSize={[18, 42]} color="secondary">
<Typography component="h1" fontSize={[18, 42]} color="secondary">
Hey there!
</Typography>
</Grid>
Expand Down
20 changes: 0 additions & 20 deletions src/components/About.tsx

This file was deleted.

13 changes: 13 additions & 0 deletions src/components/About/About.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { describe, test, expect } from "vitest";
import { screen } from "@testing-library/react";
import { renderWithQueryProvider } from "../../utils.tests";
import About from "./About";
import { ABOUT_ME_TEXT } from "../utils";

describe("<About />", () => {
test("Displays proper information", () => {
renderWithQueryProvider(<About />);
const textNode = screen.getByTestId("about");
expect(textNode.textContent).toBe(ABOUT_ME_TEXT);
});
});
14 changes: 14 additions & 0 deletions src/components/About/About.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { ABOUT_ME_TEXT } from "../utils";
import { SpotifyNowPlaying } from "../";
import { Typography, Stack } from "@mui/material";

export default function About() {
return (
<Stack spacing={1} color="info.main">
<Typography data-testid="about" fontSize={[16, 18]}>
{ABOUT_ME_TEXT}
</Typography>
<SpotifyNowPlaying />
</Stack>
);
}
41 changes: 41 additions & 0 deletions src/components/Experience/Experience.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { describe, test, expect } from "vitest";
import { screen, within } from "@testing-library/react";
import { renderWithQueryProvider } from "../../utils.tests";
import { COMPANIES } from "../../constants";
import Experience from "./Experience";

describe("<Experience />", () => {
test("Displays proper information per company", () => {
renderWithQueryProvider(<Experience />);

const chipWrappers = screen.getAllByTestId("chip-wrapper");
const jobLists = screen.getAllByTestId("jobs");

COMPANIES.forEach((company, index) => {
// Name
screen.getByText(company.name);
// Date
screen.getByText(company.duration);

// Tech as chips
const chips = within(chipWrappers[index]).getAllByTestId("chip");
expect(chips).toHaveLength(company.tech.length);

// Job descriptions
const jobListItems = within(jobLists[index]).getAllByRole("listitem");
expect(jobListItems).toHaveLength(company.jobs.length);
company.jobs.forEach((job, jobIndex) => {
expect(jobListItems[jobIndex].textContent).toBe(job);
});

// Clickable image
const iconButton = screen.getByRole("link", { name: company.name });
expect(iconButton.getAttribute("href")).toContain(company.url);
expect(iconButton.getAttribute("target")).toContain("_blank");
const img = within(iconButton)
.getByRole("img", { name: company.name })
.getAttribute("src");
expect(img).toContain(company.logo);
});
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { CompanyItem } from "../constants";
import { COMPANIES } from "../constants";
import type { CompanyItem } from "../../constants";
import { COMPANIES } from "../../constants";
import {
Timeline,
TimelineItem,
Expand All @@ -25,7 +25,7 @@ import {

const renderList = (jobs: CompanyItem["jobs"]) => {
return (
<List>
<List data-testid="jobs">
{jobs.map((job, index) => (
<ListItem key={index} sx={{ textAlign: "justify" }}>
{job}
Expand Down Expand Up @@ -57,11 +57,7 @@ export default function Experience() {
color="info.main"
minWidth={isMobile ? "100%" : undefined}
>
<Stack
spacing={1}
minWidth={[300, 0]}
alignItems={["baseline", "baseline", "end"]}
>
<Stack spacing={1} alignItems={["baseline", "baseline", "end"]}>
<Typography variant="caption">{item.duration}</Typography>
<IconButton
component="a"
Expand All @@ -73,9 +69,10 @@ export default function Experience() {
<img src={item.logo} alt={item.name} />
</IconButton>
<Typography> {item.name}</Typography>
<Box>
<Box data-testid="chip-wrapper">
{item.tech.map((tech, index) => (
<Chip
data-testid="chip"
key={index}
label={tech}
variant="outlined"
Expand Down
20 changes: 20 additions & 0 deletions src/components/Footer/Footer.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { screen, within } from "@testing-library/react";
import { renderWithQueryProvider } from "../../utils.tests";
import Footer from "./Footer";
import MTLogo from "../../assets/mt.png";

describe("<Footer />", () => {
test("Displays proper information", () => {
renderWithQueryProvider(<Footer />);

screen.getByText("© Maria Torrente 2024");

// Clickable MT Logo
const iconButton = screen.getByRole("link");
expect(iconButton.getAttribute("href")).toContain("#");
const img = within(iconButton)
.getByRole("img", { name: "Maria Torrente" })
.getAttribute("src");
expect(img).toContain(MTLogo);
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Typography, Container, Stack, IconButton } from "@mui/material";
import MTLogo from "../assets/mt.png";
import MTLogo from "../../assets/mt.png";

export default function Footer() {
return (
Expand Down
10 changes: 10 additions & 0 deletions src/components/Navigation/Navigation.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { describe, test } from "vitest";
import { renderWithQueryProvider } from "../../utils.tests";
import Navigation from "./Navigation";

// TODO: understand MUI error with `theme` usages, mainly custom properties.
describe.skip("<Navigation />", () => {
test("Displays proper information per project", () => {
renderWithQueryProvider(<Navigation />);
});
});
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import * as React from "react";
import { Box, Tab, Tabs, useMediaQuery, useTheme } from "@mui/material";
import { styled } from "@mui/system";
import { About, Experience, Projects } from "./";
import { About, Experience, Projects } from "../";

const TabsWrapper = styled(Box)(({ theme }) => ({
flexGrow: 1,
backgroundColor: theme.palette.primary.main,
color: theme.palette.secondary.main,
color: "secondary.main",
display: "flex",
marginTop: "3rem",
borderTop: `2px solid`,
Expand Down Expand Up @@ -105,7 +104,7 @@ export default function Navigation() {
const isMobile = useMediaQuery(theme.breakpoints.down("md"));

return (
<TabsWrapper>
<TabsWrapper sx={{ backgroundColor: "primary.main" }}>
<StyledTabs
orientation={isMobile ? "horizontal" : "vertical"}
variant="scrollable"
Expand Down
30 changes: 30 additions & 0 deletions src/components/Projects/Projects.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { describe, test, expect } from "vitest";
import { screen, within } from "@testing-library/react";
import { renderWithQueryProvider } from "../../utils.tests";
import { PROJECTS } from "../../constants";
import Projects from "./Projects";

describe("<Projects />", () => {
test("Displays proper information per project", () => {
renderWithQueryProvider(<Projects />);

const actions = screen.getAllByTestId("card-actions");

PROJECTS.forEach((project, index) => {
// Title
screen.getByText(project.title);
// Description
screen.getByText(project.description);

// Tech
project.tech.forEach((tech) => {
screen.getAllByText(tech);
});

// Card Actions
const footerLinks = within(actions[index]).getAllByRole("link");
expect(footerLinks[0].getAttribute("href")).toContain(project.repoLink);
expect(footerLinks[1].getAttribute("href")).toContain(project.prodLink);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ import {
CardActionArea,
IconButton,
} from "@mui/material";
import { PROJECTS } from "../constants";
import { PROJECTS } from "../../constants";
import { styled } from "@mui/system";

const StyledCard = styled(Card)(({ theme }) => ({
backgroundColor: theme.palette.primary.main,
color: "info.main",
padding: 16,
flexGrow: 1,
Expand All @@ -38,7 +37,7 @@ export default function Projects() {
spacing={3}
>
{PROJECTS.map((project, index) => (
<StyledCard key={index}>
<StyledCard key={index} sx={{ backgroundColor: "primary.main" }}>
<Box sx={{ display: "flex", flexDirection: "column" }}>
<CardActionArea
sx={{ opacity: 0.5, ":hover": { opacity: 1 } }}
Expand Down Expand Up @@ -78,7 +77,7 @@ export default function Projects() {
</Typography>
</CardContent>
</Box>
<CardActions disableSpacing>
<CardActions disableSpacing data-testid="card-actions">
<IconButton
component="a"
target="_blank"
Expand Down
23 changes: 23 additions & 0 deletions src/components/SpotifyNowPlaying/SpotifyNowPlaying.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { describe, test } from "vitest";
import { screen, waitForElementToBeRemoved } from "@testing-library/react";
import { renderWithQueryProvider } from "../../utils.tests";
import SpotifyNowPlaying from "./SpotifyNowPlaying";

describe("<SpotifyNowPLaying />", () => {
test("Displays message when no session available.", () => {
renderWithQueryProvider(<SpotifyNowPlaying />);
expect(
screen.getByText(
"Maria's tunes are on pause, she's probably rocking out at a concert!"
)
);
});

//TODO: debug why is not getting beyond getAccessToken mock.
test.skip("Displays message when session is online.", async () => {
renderWithQueryProvider(<SpotifyNowPlaying />);
await waitForElementToBeRemoved(
screen.getAllByText("Syncing to Maria's Tunes...")
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import {
Stack,
Typography,
} from "@mui/material";
import { useAccessToken, useNowPlaying } from "../services/queries";
import SpotifyIcon from "../assets/spotify.svg";
import { useAccessToken, useNowPlaying } from "../../services/queries";
import SpotifyIcon from "../../assets/spotify.svg";
import { styled } from "@mui/system";
import { getProgressPercentage } from "./utils";
import { getProgressPercentage } from "../utils";

const StyledTypography = styled(Typography)(({ theme }) => ({
maxWidth: 190,
Expand Down
12 changes: 6 additions & 6 deletions src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export { default as About } from "./About";
export { default as Navigation } from "./Navigation";
export { default as Experience } from "./Experience";
export { default as Projects } from "./Projects";
export { default as SpotifyNowPlaying } from "./SpotifyNowPlaying";
export { default as Footer } from "./Footer";
export { default as About } from "./About/About";
export { default as Navigation } from "./Navigation/Navigation";
export { default as Experience } from "./Experience/Experience";
export { default as Projects } from "./Projects/Projects";
export { default as SpotifyNowPlaying } from "./SpotifyNowPlaying/SpotifyNowPlaying";
export { default as Footer } from "./Footer/Footer";
9 changes: 9 additions & 0 deletions src/components/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,12 @@ export function getProgressPercentage(
// Ensure progress percentage is within [0, 100] range
return Math.min(100, Math.max(0, progressPercentage));
}

export const ABOUT_ME_TEXT = `In the solo dance of technology and music, I find my rhythm, weaving
code and melodies into seamless experiences that strike a chord with
users. With a passion for innovation and a knack for human connection, I
orchestrate harmonious solutions that bridge the gap between the digital
realm and the dance floor of life. Let's harmonize together,
transforming melodies into great designs, beats into rhythmic
development cycles, and progressions into solid infrastructures that
support lasting user experiences.`;
4 changes: 4 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
export const CLIENT_ID = import.meta.env.VITE_APP_SPOTIFY_CLIENT_ID;
export const CLIENT_SECRET = import.meta.env.VITE_APP_SPOTIFY_CLIENT_SECRET;
export const REFRESH_TOKEN = import.meta.env.VITE_APP_SPOTIFY_REFRESH_TOKEN;
export const ENV = import.meta.env.VITE_APP_NODE_ENV;
export const TOKEN_ENDPOINT = "https://accounts.spotify.com/api/token";
export const NOW_PLAYING_ENDPOINT =
"https://api.spotify.com/v1/me/player/currently-playing";
4 changes: 0 additions & 4 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,3 @@ export const PROJECTS: ProjectItem[] = [
tech: ["jQuery", "HTML", "CSS", "Pages"],
},
];

export const TOKEN_ENDPOINT = "https://accounts.spotify.com/api/token";
export const NOW_PLAYING_ENDPOINT =
"https://api.spotify.com/v1/me/player/currently-playing";
3 changes: 2 additions & 1 deletion src/services/spotify.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type { NowPlayingItem } from "../types/spotify";
import axios from "axios";
import { Buffer } from "buffer";
import { TOKEN_ENDPOINT, NOW_PLAYING_ENDPOINT } from "../constants";
import {
TOKEN_ENDPOINT,
NOW_PLAYING_ENDPOINT,
CLIENT_ID as clientId,
CLIENT_SECRET as clientSecret,
REFRESH_TOKEN as refreshToken,
Expand Down
Loading

0 comments on commit 1225812

Please sign in to comment.