Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
109 commits
Select commit Hold shift + click to select a range
e47ab47
Initialize React + TypeScript + Vite project with essential configura…
mariosh346 Mar 10, 2025
fb09365
Refactor App component button click handler and enhance error handlin…
mariosh346 Mar 10, 2025
7276229
Add Tailwind CSS and PostCSS configuration; update package.json depen…
mariosh346 Mar 10, 2025
f0a3d98
Add API integration for cat images and breeds; implement data parsing…
mariosh346 Mar 10, 2025
c0e7447
Add Vitest configuration and tests for cat API; implement data parsin…
mariosh346 Mar 10, 2025
cad04d1
Add unit tests for cat API and parsers; implement mocking and error h…
mariosh346 Mar 11, 2025
d207362
Add favorites functionality and image viewing; implement hooks for fe…
mariosh346 Mar 11, 2025
a0fd274
Add favorites view and functionality; implement navigation links, rem…
mariosh346 Mar 11, 2025
5d19fe0
Add breeds functionality; implement breeds view, breed detail view, a…
mariosh346 Mar 11, 2025
c5655dd
Refactor TypeScript configuration; improve formatting and add consist…
mariosh346 Mar 11, 2025
da1cc65
Refactor tests and mocks; remove unused test files, update axios mock…
mariosh346 Mar 11, 2025
27ec97a
change document title to "Marios' Cat Api", pass image data through …
mariosh346 Mar 11, 2025
6548986
Add image fetching by ID and enhance Modal for loading and error states
mariosh346 Mar 11, 2025
5d8311e
Refactor ImageGallery and Breeds components for performance; implemen…
mariosh346 Mar 11, 2025
df3c762
Enhance ImageGallery and Favorites components; add renderAfterImage p…
mariosh346 Mar 11, 2025
6a9f759
Optimize image fetching functions with useCallback for improved perfo…
mariosh346 Mar 12, 2025
d39b95a
Add breed fetching by ID and enhance BreedDetail component with loadi…
mariosh346 Mar 12, 2025
15f045c
Enhance Modal component with improved loading and error message styli…
mariosh346 Mar 12, 2025
42d12a3
Add API functions for fetching images and breeds by ID; enhance tests…
mariosh346 Mar 12, 2025
03674c8
Add Cypress configuration and tests for breeds and home pages; update…
mariosh346 Mar 12, 2025
30619a2
Update ESLint config for Cypress support
mariosh346 Mar 12, 2025
abca2dc
Rename "Load New" button to "Load More" and update image fetching log…
mariosh346 Mar 12, 2025
f03c86a
Reorder loading and error messages in Home component for improved use…
mariosh346 Mar 12, 2025
2ec6f46
Refactor Cypress configuration: migrate from cypress.config.js to cyp…
mariosh346 Mar 12, 2025
8ef1b5a
Update README.md with setup instructions, testing guidelines, and tec…
mariosh346 Mar 12, 2025
38c098b
Add deployment instructions and update configuration for GitHub Pages
mariosh346 Mar 12, 2025
0e8592f
Fix paths for favicon and main script in index.html; set basename for…
mariosh346 Mar 12, 2025
d124429
Add BASENAME_ROUTE constant and update routing configuration; refacto…
mariosh346 Mar 12, 2025
8efe65f
Update README.md with next steps
mariosh346 Mar 13, 2025
b1d79fd
Implement code splitting with lazy loading for views; enhance navigat…
mariosh346 Mar 13, 2025
bc70468
Add axios-cache-interceptor for caching API responses; update catApi …
mariosh346 Mar 13, 2025
0b606e8
Add next step to README.md
mariosh346 Mar 13, 2025
864197d
Update Modal component styles and adjust ImageView image class
mariosh346 Mar 13, 2025
34ca762
Add CI workflow for testing and deployment; include setup script for …
mariosh346 Mar 27, 2025
648296e
Bump vite in the npm_and_yarn group across 1 directory
dependabot[bot] Mar 27, 2025
b9a183e
Bump @babel/runtime in the npm_and_yarn group across 1 directory
dependabot[bot] Mar 27, 2025
35dfe86
Merge pull request #2 from mariosh346/dependabot/npm_and_yarn/npm_and…
mariosh346 Mar 27, 2025
b77262f
Merge pull request #1 from mariosh346/dependabot/npm_and_yarn/npm_and…
mariosh346 Mar 27, 2025
983b3f3
Update GitHub Actions workflow to set permissions for pull requests a…
mariosh346 Mar 27, 2025
71fd661
Merge branch 'main' of https://github.com/mariosh346/platform-catApi-…
mariosh346 Mar 27, 2025
d20958c
Refactor GitHub Actions workflow to use a composite action for shared…
mariosh346 Mar 27, 2025
c9a9263
Refactor GitHub Actions setup by moving composite action to a dedicat…
mariosh346 Mar 27, 2025
2ffb7aa
Refactor GitHub Actions deploy workflow to use a common anchor for ch…
mariosh346 Mar 27, 2025
0c5e7a7
Refactor GitHub Actions workflow to consolidate checkout steps and st…
mariosh346 Mar 27, 2025
ae8e863
Refactor GitHub Actions deploy workflow to use explicit checkout steps
mariosh346 Mar 27, 2025
ecf8fcf
Refactor GitHub Actions deploy workflow by removing redundant setup job
mariosh346 Mar 27, 2025
6aa31b0
Update GitHub Actions workflow to use actions/checkout@v4 and replace…
mariosh346 Mar 27, 2025
b1e3270
Add engines field to package.json specifying Node and pnpm versions
mariosh346 Mar 27, 2025
17d05b2
Remove dependency on vitest and cypress from build-deploy job in GitH…
mariosh346 Mar 29, 2025
93ba15d
Fix deploy command in GitHub Actions workflow to use 'pnpm run deploy'
mariosh346 Mar 29, 2025
a072c3c
Refactor API calls to use cached axios instance and update and fix tests
mariosh346 Mar 29, 2025
bfea109
Add vitest to needs of depployment
mariosh346 Mar 29, 2025
99145dc
Transform tests to js
mariosh346 Mar 29, 2025
931a20b
Update deployment workflow to use Ubuntu 22
mariosh346 Mar 29, 2025
d2d6aa2
Add Cypress binary installation step to deployment workflow
mariosh346 Mar 29, 2025
8b1cf48
Update deployment workflow to use Ubuntu 22 for all jobs and adjust C…
mariosh346 Mar 29, 2025
a8eb86a
Update deployment workflow to use ubuntu-latest for all jobs to unblo…
mariosh346 Mar 29, 2025
3ddfeaa
Update deployment workflow to use Ubuntu 22.04 for all jobs
mariosh346 Mar 29, 2025
8681700
Update Cypress start command to specify port 5173 in deployment workflow
mariosh346 Mar 29, 2025
81834de
Set Git commit identity in deployment workflow
mariosh346 Mar 30, 2025
c46760a
Add cypress/screenshots/ to .gitignore
mariosh346 Mar 30, 2025
540e743
Refactor axios caching setup to use a dedicated getAxios function
mariosh346 Mar 30, 2025
988a7a9
Refactor deployment workflow and remove obsolete test snapshot
mariosh346 Mar 30, 2025
ed7aa41
Create jekyll-gh-pages.yml
mariosh346 Mar 30, 2025
e6e7c31
Update deployment workflow to use Ubuntu 22.04 runner
mariosh346 Mar 30, 2025
247d63e
Merge branch 'main' of https://github.com/mariosh346/platform-catApi-…
mariosh346 Mar 30, 2025
4d1246d
Update deployment workflows: add artifact upload step and remove obso…
mariosh346 Mar 30, 2025
5787a59
Run build on build step: add manual trigger and concurrency settings
mariosh346 Mar 30, 2025
ca86d06
Update deployment workflow: add artifact download step for distribution
mariosh346 Mar 30, 2025
3c3370a
Update deployment workflow: downgrade artifact download action to v2
mariosh346 Mar 30, 2025
e8e03c6
Update deployment workflow: remove artifact download step and adjust …
mariosh346 Mar 30, 2025
fdb9acd
Update deployment workflow: adjust GitHub Pages URL for platform-catA…
mariosh346 Mar 30, 2025
cbbe65e
Update deployment workflow: switch to ubuntu-latest and upgrade Node.…
mariosh346 Mar 30, 2025
ecb3cf6
Update deployment workflow: use docker Node.js 22-alpine container fo…
mariosh346 Mar 30, 2025
c1af50f
Update deployment workflow: switch Node.js image from 22-alpine to 22
mariosh346 Mar 30, 2025
3630670
Update deployment workflow: switch Node.js image to 22-alpine for all…
mariosh346 Mar 30, 2025
4d2dc72
Update deployment workflow: revert from docker to gh
mariosh346 Mar 30, 2025
0da78d3
Bump vite in the npm_and_yarn group across 1 directory
dependabot[bot] Mar 31, 2025
509f8cd
make home cats fetch only once
mariosh346 Apr 1, 2025
0f3f043
Update deployment workflow: add baseUrl configuration for ci cy tests
mariosh346 Apr 7, 2025
9664a3a
Merge branch 'main' of https://github.com/mariosh346/platform-catApi-…
mariosh346 Apr 7, 2025
db3867b
Merge pull request #3 from mariosh346/dependabot/npm_and_yarn/npm_and…
mariosh346 Apr 7, 2025
5ddb822
Bump vite in the npm_and_yarn group across 1 directory
dependabot[bot] Apr 7, 2025
4e79546
Merge pull request #4 from mariosh346/dependabot/npm_and_yarn/npm_and…
mariosh346 Apr 8, 2025
5af6d39
Update deployment workflow to include cypress as a dependency for the…
mariosh346 Apr 8, 2025
f47cb2c
Add SSH key setup instructions to README
mariosh346 Apr 8, 2025
3f42509
Add instructions for auto-signing commits and tags in README
mariosh346 Apr 9, 2025
aab4822
Bump vite in the npm_and_yarn group across 1 directory
dependabot[bot] Apr 11, 2025
fa830ed
Update README to reflect deployment process on GitHub Pages using Git…
mariosh346 Apr 19, 2025
7b5f83e
Update README to reflect project name change and streamline content
mariosh346 Apr 19, 2025
338229f
Merge pull request #5 from mariosh346/dependabot/npm_and_yarn/npm_and…
mariosh346 Apr 19, 2025
ba4f514
Update react-router-dom to version 7.6.0
mariosh346 May 20, 2025
2b589c5
Merge branch 'main' of https://github.com/mariosh346/platform-catApi-…
mariosh346 May 20, 2025
fa647c4
Update vite dependency from 6.2.6 to 6.3.5
mariosh346 May 20, 2025
a8a7d94
Bump brace-expansion in the npm_and_yarn group across 1 directory
dependabot[bot] Jun 12, 2025
fbd124e
Merge pull request #7 from mariosh346/dependabot/npm_and_yarn/npm_and…
mariosh346 Sep 19, 2025
20e19ec
Bump the npm_and_yarn group across 1 directory with 4 updates
dependabot[bot] Sep 19, 2025
ea87c47
Merge pull request #8 from mariosh346/dependabot/npm_and_yarn/npm_and…
mariosh346 Sep 19, 2025
c2bf947
Splitting files into more files
mariosh346 Sep 19, 2025
c88320a
Fix cy tests
mariosh346 Sep 20, 2025
ebda36e
Refactor components to improve loading states and error handling; rep…
mariosh346 Sep 20, 2025
f6faffe
Enhance loading states in ImageGallery and Modal components
mariosh346 Sep 22, 2025
bda36a0
Update ImageCard layout to use flex-col for better alignment
mariosh346 Sep 22, 2025
5b0c35a
add data attributes for Cypress testing
mariosh346 Sep 22, 2025
2e45fac
Add tests for Favorites page and refactor Home page tests for reusabi…
mariosh346 Sep 22, 2025
90b5087
Update snapshot
mariosh346 Sep 22, 2025
d1d6b2a
Merge pull request #9 from mariosh346/refactoring
mariosh346 Sep 22, 2025
f2e5f72
Refactor imports to remove React from component files for cleaner code
mariosh346 Sep 22, 2025
0c5f58a
Merge pull request #10 from mariosh346/refactoring
mariosh346 Sep 22, 2025
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
8 changes: 8 additions & 0 deletions .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
name: "My Composite Action"
description: "Runs shared setup commands for CI jobs"
inputs: {}
runs:
using: "composite"
steps:
- name: Setup Environment
run: bash scripts/setup.sh
81 changes: 81 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
name: CI & Deploy

on:
push:
branches: [ main ]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

permissions:
contents: read

concurrency:
group: "pages"
cancel-in-progress: false

jobs:
vitest:
runs-on: ubuntu-latest
steps:
- uses: actions/setup-node@v4
with:
node-version: '22'
- name: checkout repository
uses: actions/checkout@v4
- name: setup environment
run: bash scripts/setup.sh
- name: Run Vitest Tests
run: pnpm test

cypress:
runs-on: ubuntu-latest
steps:
- uses: actions/setup-node@v4
with:
node-version: '22'
- name: checkout repository
uses: actions/checkout@v4
- name: setup environment
run: bash scripts/setup.sh
- name: Install Cypress Binary
run: npx cypress install
- name: Cypress run
uses: cypress-io/github-action@v6
with:
build: pnpm build
start: pnpm preview --port 5173
config: 'baseUrl=http://localhost:5173/platform-catApi-react'

build:
runs-on: ubuntu-latest
needs: [vitest, cypress]
steps:
- uses: actions/setup-node@v4
with:
node-version: '22'
- name: checkout repository
uses: actions/checkout@v4
- name: setup environment
run: bash scripts/setup.sh
- name: build environment
run: pnpm build
- name: Upload Artifact
uses: actions/upload-pages-artifact@v3
with:
path: ./dist

deploy:
needs: build

permissions:
pages: write
id-token: write

environment:
name: github-pages
url: "${{ steps.deployment.outputs.page_url }}/platform-catApi-react/"
runs-on: ubuntu-latest
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
31 changes: 31 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

*.mp4
*.mov
*.avi
*.mkv

cypress/screenshots/

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
91 changes: 82 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# GlobalWebIndex Engineering Challenge
## Project: CatLover

## Exercise: CatLover

Create a React application for cat lovers which is going to build upon thecatapi.com and will have 3 views.
A React application for cat lovers which is going to build upon thecatapi.com and will have 3 views.
The **first** view displays a list of 10 random cat images and a button to load more. Clicking on any of those images opens a modal view with the image and the information about the cat’s breed if available. This would be a link to the second view below - the breed detail. The modal should also contain a form to mark the image as your favourite (a part of the third view as well). Make sure you can copy-paste the URL of the modal and send it to your friends - they should see the same image as you can see.

The **second** view displays a list of cat breeds. Each breed opens a modal again with a list of cat images of that breed. Each of those images must be a link to the image detail from the previous point.
Expand All @@ -12,10 +10,85 @@ The **third** view allows you do the following things:
- Display your favourite cats
- Remove an image from your favourites (use any UX option you like)

You can find the API documentation here: https://developers.thecatapi.com/
We give you a lot of freedom in technologies and ways of doing things. We only insist on you using React.js. Get creative as much as you want, we WILL appreciate it. You will not be evaluated based on how well you follow these instructions, but based on how sensible your solution will be. In case you are not able to implement something you would normally implement for time reasons, make it clear with a comment.
API documentation here: https://developers.thecatapi.com/

# Solution
Deploying after every commit on GitHub Pages at https://mariosh346.github.io/platform-catApi-react/ with the help of Github Actions

## Prerequisites
- nvm (Node Version Manager) or Node v22

## Setup

1. Clone the repository:
```bash
git clone https://github.com/mariosh346/platform-catApi-react.git
cd platform-catApi-react
```

2. Install Node.js using nvm:
```bash
nvm install 22
nvm use 22
```

3. Install pnpm:
```bash
npm install -g pnpm
```

4. Install dependencies using pnpm:
```bash
pnpm install
```

## Running the Application

- Start the development server:
```bash
pnpm dev
```

- Build for production:
```bash
pnpm build
```

## Commit
1. create ssh keys https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent
2. git config --global gpg.format ssh
3. git config --global user.signingkey git config --global user.signingkey C:\Users\mario\.ssh\id_XXXX.pub
4. Add the .pub data to https://github.com/settings/keys as signing key
5. Auto sign all commits ```bash
git config --global commit.gpgsign true
git config --global tag.gpgSign true
```

## Testing

Run Cypress tests:
```bash
pnpm cypress
```

## Deployment

To deploy the project to GitHub Pages, run the following command:

```sh
pnpm deploy
```

Note: Once deployed, check the live site on GitHub Pages at https://mariosh346.github.io/platform-catApi-react/

## Next steps

- Add state management like redux
- Introduce small reusable components like Button, Link

## Submission
## Tech Stack

Once you have built your app, share your code in the mean suits you best
Good luck, potential colleague!
- React
- TypeScript
- Vite
- Cypress
9 changes: 9 additions & 0 deletions cypress.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { defineConfig } from 'cypress'

export default defineConfig({
e2e: {
baseUrl: 'http://localhost:5173',
specPattern: 'cypress/e2e/**/*.{js,jsx,ts,tsx}',
supportFile: 'cypress/support/e2e.ts',
},
})
18 changes: 18 additions & 0 deletions cypress/e2e/breeds.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
describe('Breeds Page', () => {
beforeEach(() => {
cy.visit('/breeds')
})

it('displays the header "Cat Breeds"', () => {
cy.get('h1').contains('Cat Breeds').should('exist')
})

it('renders at least one breed list item', () => {
cy.get('ul li').its('length').should('be.gt', 0)
})

it('navigates to breed detail on breed click', () => {
cy.get('ul li').first().click();
cy.url().should('include', '/breed-detail/');
});
})
6 changes: 6 additions & 0 deletions cypress/e2e/favorites.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
describe('Favorites Page Tests', () => {
it('displays "No favorite cats yet." when favorites are empty', () => {
cy.visit('/favorites');
cy.get('[data-cy="no-favorites-message"]').contains('No favorite cats yet.').should('exist');
});
});
92 changes: 92 additions & 0 deletions cypress/e2e/home.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// eslint-disable-next-line @typescript-eslint/triple-slash-reference
/// <reference types="cypress" />

const visitHomePage = () => {
cy.visit('/');
};

describe('Home Page Tests', () => {
beforeEach(() => {
cy.clearLocalStorage('favorites'); // Clear favorites before each test
});

it('displays the header "Random Cats"', () => {
visitHomePage();
cy.get('[data-cy="home-header"]').contains('Random Cats').should('exist');
});

it('loads images and shows the "Load More" button', () => {
visitHomePage();
cy.get('[data-cy="image-card-image"]').should('have.length.greaterThan', 0);
cy.get('[data-cy="load-more-button"]').should('exist');
});

it('displays skeleton loaders while fetching images', () => {
cy.intercept('GET', '**/images/search?limit=10').as('getCatImagesDelayed');
cy.visit('/');
cy.get('[data-cy="skeleton-loader"]').should('have.length', 10); // Check for 10 skeleton cards
cy.wait('@getCatImagesDelayed');
cy.get('[data-cy="skeleton-loader"]').should('not.exist');
cy.get('[data-cy="image-card-image"]').should('have.length.greaterThan', 0);
});

it('displays an error message on API failure', () => {
cy.intercept('GET', '**/images/search?limit=10', { statusCode: 500, body: 'Internal Server Error' }).as('getCatImagesError');
cy.visit('/');
cy.wait('@getCatImagesError');
cy.get('[data-cy="error-message"]').contains('Failed to load images.').should('exist');
cy.get('[data-cy="load-more-button"]').should('not.be.disabled'); // Button should be enabled to allow retry
});

it('displays "No images found." when API returns empty data', () => {
cy.intercept('GET', '**/images/search?limit=10', { body: [] }).as('getCatImagesEmpty');
cy.visit('/');
cy.wait('@getCatImagesEmpty');
cy.get('[data-cy="no-images-found-message"]').contains('No images found.').should('exist');
cy.get('[data-cy="load-more-button"]').should('exist'); // Button should still be there to try again
});

it('navigates to image detail on image click', () => {
visitHomePage();
cy.get('[data-cy="image-card-image"]').first().click();
cy.url().should('include', '/image/');
cy.get('[role="dialog"]').should('be.visible');
});

it('closes the modal on Escape key press', () => {
visitHomePage();
cy.get('[data-cy="image-card-image"]').first().click();
cy.get('[role="dialog"]').should('be.visible');
cy.realPress('{esc}'); // Use cypress-real-events for Escape key
cy.get('[role="dialog"]').should('not.exist');
});

it('adds and removes image from favorites', () => {
visitHomePage();
cy.get('[data-cy="image-card-image"]').first().click();
cy.get('[role="dialog"]').should('be.visible');

// Mark as favorite
cy.get('[data-cy="mark-as-favourite-button"]').click();
cy.get('[data-cy="remove-from-favourites-button"]').should('exist');
cy.realPress('{esc}'); // Close modal

// Navigate to favorites page
cy.get('[data-cy="favorites-link"]').click();
cy.url().should('include', '/favorites');
cy.get('[data-cy="image-card-image"]').should('have.length', 1); // Should have 1 favorite image

// Open the favorite image from the favorites page
cy.get('[data-cy="image-card-image"]').first().click();
cy.get('[role="dialog"]').should('be.visible');
cy.get('[data-cy="remove-from-favourites-button"]').should('exist');

// Remove from favorites
cy.get('[data-cy="remove-from-favourites-button"]').click();
cy.get('[data-cy="mark-as-favourite-button"]').should('exist'); // Button text changes back
cy.get('[aria-label="Close modal"]').click(); // Close modal by clicking the close button

// Verify no favorites
cy.get('[data-cy="no-favorites-message"]').contains('No favorite cats yet.').should('exist');
});
});
Loading