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