initial boilerplate code
All checks were successful
E2E Tests with Cypress / cypress (push) Successful in 3m12s
All checks were successful
E2E Tests with Cypress / cypress (push) Successful in 3m12s
This commit is contained in:
commit
a9b8e7bc9e
23 changed files with 3949 additions and 0 deletions
8
.dockerignore
Normal file
8
.dockerignore
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
*
|
||||
!src/
|
||||
!package.json
|
||||
!yarn.lock
|
||||
!.env*
|
||||
!next-env.d.ts
|
||||
!next.config.ts
|
||||
!tsconfig.json
|
||||
3
.env.example
Normal file
3
.env.example
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# create .env.development, .env.production and .env.test
|
||||
|
||||
# placeholder, no variables required for now
|
||||
3
.eslintrc.json
Normal file
3
.eslintrc.json
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"extends": ["next/core-web-vitals", "next/typescript", "prettier"]
|
||||
}
|
||||
71
.forgejo/workflows/cypress.yaml
Normal file
71
.forgejo/workflows/cypress.yaml
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
name: E2E Tests with Cypress
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- dev
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
cypress:
|
||||
runs-on: ubuntu-host-docker
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Start test app with Docker Compose
|
||||
run: docker compose up --build -d app-test
|
||||
|
||||
- name: Wait for test app to be healthy
|
||||
run: |
|
||||
SERVICE_NAME="app-test"
|
||||
# wait for the container to exist
|
||||
until CONTAINER_ID=$(docker compose ps -q $SERVICE_NAME 2>/dev/null) && [ -n "$CONTAINER_ID" ]; do
|
||||
echo "Waiting for $SERVICE_NAME container to be created..."
|
||||
sleep 5
|
||||
done
|
||||
# now check health status
|
||||
while [ "$(docker inspect -f '{{.State.Health.Status}}' $CONTAINER_ID)" != "healthy" ]; do
|
||||
echo "Waiting for $SERVICE_NAME to become healthy..."
|
||||
sleep 5
|
||||
done
|
||||
echo "$SERVICE_NAME is healthy!"
|
||||
|
||||
- name: Build Docker Cypress image
|
||||
run: |
|
||||
tar -ch \
|
||||
cypress.config.ts \
|
||||
cypress/e2e \
|
||||
cypress/tsconfig.json \
|
||||
tsconfig.json \
|
||||
Dockerfile.cypress \
|
||||
| docker build -f Dockerfile.cypress -t cypress-tests -
|
||||
|
||||
- name: Run Cypress tests with artifact collection
|
||||
run: |
|
||||
mkdir -p cypress-videos cypress-screenshots
|
||||
docker run --network host \
|
||||
-v $(pwd)/cypress-videos:/e2e/cypress/videos \
|
||||
-v $(pwd)/cypress-screenshots:/e2e/cypress/screenshots \
|
||||
-t cypress-tests || true
|
||||
|
||||
- name: Upload test artifacts
|
||||
uses: https://data.forgejo.org/forgejo/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: cypress-artifacts.zip
|
||||
path: |
|
||||
cypress-videos/
|
||||
cypress-screenshots/
|
||||
retention-days: 3
|
||||
|
||||
- name: Cleanup Docker resources
|
||||
if: always()
|
||||
run: |
|
||||
docker compose down --volumes --rmi local --remove-orphans
|
||||
docker system prune -af \
|
||||
--filter "label!=production"
|
||||
44
.gitignore
vendored
Normal file
44
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.*
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/versions
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# env files (can opt-in for committing if needed)
|
||||
.env*
|
||||
|
||||
# include the example environment file
|
||||
!.env.example
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
||||
# cypress
|
||||
cypress/screenshots/
|
||||
cypress/videos/
|
||||
cypress/reports/
|
||||
cypress-screenshots/
|
||||
cypress-videos/
|
||||
2
.prettierignore
Normal file
2
.prettierignore
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
node_modules
|
||||
.next
|
||||
8
.prettierrc
Normal file
8
.prettierrc
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "es5",
|
||||
"tabWidth": 2,
|
||||
"printWidth": 80,
|
||||
"arrowParens": "always"
|
||||
}
|
||||
14
.vscode/settings.json
vendored
Normal file
14
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"editor.formatOnSave": true, // Prettier will format on save
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode", // Use Prettier for formatting
|
||||
"eslint.format.enable": false, // Don't let ESLint format
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit", // Run ESLint fixes on save
|
||||
"source.organizeImports": "explicit" // Organize imports on save
|
||||
},
|
||||
"editor.quickSuggestions": {
|
||||
"other": true,
|
||||
"comments": false,
|
||||
"strings": true
|
||||
}
|
||||
}
|
||||
17
Dockerfile
Normal file
17
Dockerfile
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# base stage
|
||||
FROM node:22-alpine AS base
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
# add curl for healthchecks
|
||||
RUN apk add --no-cache curl
|
||||
|
||||
# development mode stage
|
||||
FROM base AS dev
|
||||
RUN yarn install --frozen-lockfile
|
||||
CMD ["yarn", "dev"]
|
||||
|
||||
# production mode stage
|
||||
FROM base AS prod
|
||||
RUN yarn install --production --frozen-lockfile
|
||||
RUN yarn build
|
||||
CMD ["yarn", "start"]
|
||||
12
Dockerfile.cypress
Normal file
12
Dockerfile.cypress
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
FROM cypress/browsers:latest
|
||||
|
||||
WORKDIR /e2e
|
||||
|
||||
COPY tsconfig.json .
|
||||
COPY cypress.config.ts .
|
||||
COPY cypress ./cypress
|
||||
|
||||
RUN yarn init -y && \
|
||||
yarn add -D cypress typescript
|
||||
|
||||
CMD ["npx", "cypress", "run"]
|
||||
22
LICENSE.md
Normal file
22
LICENSE.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# License for Viewing and Learning
|
||||
|
||||
Copyright (c) 2025 Aleksa Bandic
|
||||
|
||||
This code is publicly available for **viewing and learning purposes**. You are welcome to explore it, study it, and use it as inspiration to improve your own skills.
|
||||
|
||||
However, please note the following restrictions:
|
||||
|
||||
1. **No Unauthorized Use**
|
||||
|
||||
- This code **may not** be copied, modified, or used in personal, public, or commercial projects without explicit permission.
|
||||
|
||||
2. **No Redistribution**
|
||||
|
||||
- You may **not** re-upload or share this code as your own in any form.
|
||||
|
||||
3. **Learning Purposes Only**
|
||||
- You are encouraged to reference this code to understand concepts, but direct reuse in your own public projects is **not allowed**.
|
||||
|
||||
If you're interested in using this code in a project or have any questions, feel free to reach out!
|
||||
|
||||
📩 Contact: aleksa@aleksa.sh
|
||||
31
README.md
Normal file
31
README.md
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# aleksa.sh
|
||||
|
||||

|
||||
|
||||
my personal website and a sandbox to practice react/next.js and setting up + deploying relevant projects.
|
||||
|
||||
## running
|
||||
|
||||
requires docker and docker compose (optionally node + yarn to use cypress without docker)
|
||||
|
||||
run dev (w/ hot reload) with `docker compose up app-dev`
|
||||
|
||||
run prod with `docker compose up app-prod`
|
||||
|
||||
for e2e, run the app in test mode with `docker compose up app-test`, then start a native cypress run with `yarn e2e`. for a docker cypress run instead, use a tarball with custom build context to circumvent the default .dockerignore file like in the ci example.
|
||||
|
||||
test expansion is planned for the future.
|
||||
|
||||
## ci/cd
|
||||
|
||||
forgejo runner used (labelled `ubuntu-host-docker`) is a host passthrough runner that has access to an ubuntu host machine with docker and docker compose installed. refer to this [forgejo actions documentation](https://forgejo.org/docs/latest/admin/actions/#host) for more info.
|
||||
|
||||
make sure to always have a cleanup task at the end to prevent piling up containers on the host machine.
|
||||
|
||||
the ci workflow syntax and tasks may undergo frequent changes due to forgejo actions being wip.
|
||||
|
||||
future commits will introduce auto-deployment in cd.
|
||||
|
||||
## contact
|
||||
|
||||
feel free to reach out to me for any purpose on aleksa@aleksa.sh :)
|
||||
11
cypress.config.ts
Normal file
11
cypress.config.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import { defineConfig } from 'cypress';
|
||||
|
||||
export default defineConfig({
|
||||
e2e: {
|
||||
supportFile: false,
|
||||
baseUrl: 'http://localhost:3001',
|
||||
},
|
||||
video: true,
|
||||
screenshotsFolder: 'cypress/screenshots',
|
||||
videosFolder: 'cypress/videos',
|
||||
});
|
||||
8
cypress/e2e/homepage.cy.ts
Normal file
8
cypress/e2e/homepage.cy.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
describe('Homepage', () => {
|
||||
it('should load successfully', () => {
|
||||
cy.visit('/');
|
||||
cy.contains('aleksa.sh').should('exist');
|
||||
cy.screenshot();
|
||||
cy.wait(500);
|
||||
});
|
||||
});
|
||||
14
cypress/tsconfig.json
Normal file
14
cypress/tsconfig.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["es5", "dom"],
|
||||
"types": ["cypress"],
|
||||
"baseUrl": "../",
|
||||
"noEmit": true,
|
||||
"isolatedModules": false,
|
||||
"moduleResolution": "node"
|
||||
},
|
||||
"include": ["**/*.ts"],
|
||||
"exclude": []
|
||||
}
|
||||
41
docker-compose.yaml
Normal file
41
docker-compose.yaml
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
x-common: &common # shared values
|
||||
build: &build-base
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
ports: &ports
|
||||
- '3000:3000'
|
||||
healthcheck:
|
||||
&healthcheck # curl outputs to /dev/null to prevent clutter in logs
|
||||
test: ['CMD', 'curl', '-sf', 'http://localhost:3000', '-o', '/dev/null']
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 1
|
||||
start_period: 3s
|
||||
|
||||
services:
|
||||
app-dev:
|
||||
build:
|
||||
<<: *build-base
|
||||
target: dev
|
||||
volumes:
|
||||
- ./src:/app/src # enables hot reload
|
||||
- /app/node_modules # keeps installed node_modules
|
||||
ports: *ports
|
||||
healthcheck: *healthcheck
|
||||
|
||||
app-prod: &app-prod
|
||||
labels:
|
||||
- 'production'
|
||||
build:
|
||||
<<: *build-base
|
||||
target: prod
|
||||
ports: *ports
|
||||
healthcheck: *healthcheck
|
||||
|
||||
app-test:
|
||||
<<: *app-prod
|
||||
environment:
|
||||
- NODE_ENV=test # override environment variables
|
||||
ports:
|
||||
- '3001:3000' # override ports to prevent conflicts while testing
|
||||
healthcheck: *healthcheck # uses internal port 3000, not host 3001
|
||||
7
next.config.ts
Normal file
7
next.config.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import type { NextConfig } from 'next';
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
/* config options here */
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
29
package.json
Normal file
29
package.json
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"name": "aleksa.sh",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"e2e": "cypress run"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.8.4",
|
||||
"next": "^15.3.0",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"cypress": "^14.3.0",
|
||||
"eslint": "^9.24.0",
|
||||
"eslint-config-next": "15.3.0",
|
||||
"eslint-config-prettier": "^10.1.2",
|
||||
"prettier": "3.5.3",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
}
|
||||
19
src/app/layout.tsx
Normal file
19
src/app/layout.tsx
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import type { Metadata } from 'next';
|
||||
import '../styles/globals.css';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'aleksa.sh',
|
||||
description: 'My personal website and sandbox project',
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html>
|
||||
<body>{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
13
src/app/page.tsx
Normal file
13
src/app/page.tsx
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
'use client'; //mark as client app
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="container">
|
||||
<main>
|
||||
<h1 className="title">aleksa.sh</h1>
|
||||
<p className="subTitle">coming soon</p>
|
||||
</main>
|
||||
<footer></footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
41
src/styles/globals.css
Normal file
41
src/styles/globals.css
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
:root {
|
||||
--background: #ffffff;
|
||||
--foreground: #171717;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--background: #171717;
|
||||
--foreground: #ededed;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
color: var(--foreground);
|
||||
background: var(--background);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 2rem;
|
||||
margin: 0;
|
||||
padding-bottom: 0.5rem;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
}
|
||||
|
||||
.subTitle {
|
||||
font-size: 1.3rem;
|
||||
margin: 0;
|
||||
color: gray;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
}
|
||||
28
tsconfig.json
Normal file
28
tsconfig.json
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2017",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"baseUrl": ".",
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue