diff --git a/apps/url-server2-e2e/.eslintrc.json b/apps/url-server2-e2e/.eslintrc.json new file mode 100644 index 0000000..8852e20 --- /dev/null +++ b/apps/url-server2-e2e/.eslintrc.json @@ -0,0 +1,10 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/apps/url-server2-e2e/jest.config.ts b/apps/url-server2-e2e/jest.config.ts new file mode 100644 index 0000000..c974a19 --- /dev/null +++ b/apps/url-server2-e2e/jest.config.ts @@ -0,0 +1,19 @@ +/* eslint-disable */ +export default { + displayName: 'url-server2-e2e', + preset: '../../jest.preset.js', + globalSetup: '/src/support/global-setup.ts', + globalTeardown: '/src/support/global-teardown.ts', + setupFiles: ['/src/support/test-setup.ts'], + testEnvironment: 'node', + transform: { + '^.+\\.[tj]s$': [ + 'ts-jest', + { + tsconfig: '/tsconfig.spec.json', + }, + ], + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '../../coverage/url-server2-e2e', +}; diff --git a/apps/url-server2-e2e/project.json b/apps/url-server2-e2e/project.json new file mode 100644 index 0000000..009ae32 --- /dev/null +++ b/apps/url-server2-e2e/project.json @@ -0,0 +1,22 @@ +{ + "name": "url-server2-e2e", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "implicitDependencies": ["url-server2"], + "targets": { + "e2e": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{e2eProjectRoot}"], + "options": { + "jestConfig": "apps/url-server2-e2e/jest.config.ts", + "passWithNoTests": true + } + }, + "lint": { + "executor": "@nx/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["apps/url-server2-e2e/**/*.{js,ts}"] + } + } + } +} diff --git a/apps/url-server2-e2e/src/support/global-setup.ts b/apps/url-server2-e2e/src/support/global-setup.ts new file mode 100644 index 0000000..c1f5144 --- /dev/null +++ b/apps/url-server2-e2e/src/support/global-setup.ts @@ -0,0 +1,10 @@ +/* eslint-disable */ +var __TEARDOWN_MESSAGE__: string; + +module.exports = async function () { + // Start services that that the app needs to run (e.g. database, docker-compose, etc.). + console.log('\nSetting up...\n'); + + // Hint: Use `globalThis` to pass variables to global teardown. + globalThis.__TEARDOWN_MESSAGE__ = '\nTearing down...\n'; +}; diff --git a/apps/url-server2-e2e/src/support/global-teardown.ts b/apps/url-server2-e2e/src/support/global-teardown.ts new file mode 100644 index 0000000..32ea345 --- /dev/null +++ b/apps/url-server2-e2e/src/support/global-teardown.ts @@ -0,0 +1,7 @@ +/* eslint-disable */ + +module.exports = async function () { + // Put clean up logic here (e.g. stopping services, docker-compose, etc.). + // Hint: `globalThis` is shared between setup and teardown. + console.log(globalThis.__TEARDOWN_MESSAGE__); +}; diff --git a/apps/url-server2-e2e/src/support/test-setup.ts b/apps/url-server2-e2e/src/support/test-setup.ts new file mode 100644 index 0000000..07f2870 --- /dev/null +++ b/apps/url-server2-e2e/src/support/test-setup.ts @@ -0,0 +1,10 @@ +/* eslint-disable */ + +import axios from 'axios'; + +module.exports = async function () { + // Configure axios for tests to use. + const host = process.env.HOST ?? 'localhost'; + const port = process.env.PORT ?? '3000'; + axios.defaults.baseURL = `http://${host}:${port}`; +}; diff --git a/apps/url-server2-e2e/src/url-server2/url-server2.spec.ts b/apps/url-server2-e2e/src/url-server2/url-server2.spec.ts new file mode 100644 index 0000000..51717c7 --- /dev/null +++ b/apps/url-server2-e2e/src/url-server2/url-server2.spec.ts @@ -0,0 +1,10 @@ +import axios from 'axios'; + +describe('GET /', () => { + it('should return a message', async () => { + const res = await axios.get(`/`); + + expect(res.status).toBe(200); + expect(res.data).toEqual({ message: 'Hello API' }); + }); +}); diff --git a/apps/url-server2-e2e/tsconfig.json b/apps/url-server2-e2e/tsconfig.json new file mode 100644 index 0000000..ed633e1 --- /dev/null +++ b/apps/url-server2-e2e/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.spec.json" + } + ], + "compilerOptions": { + "esModuleInterop": true + } +} diff --git a/apps/url-server2-e2e/tsconfig.spec.json b/apps/url-server2-e2e/tsconfig.spec.json new file mode 100644 index 0000000..d7f9cf2 --- /dev/null +++ b/apps/url-server2-e2e/tsconfig.spec.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": ["jest.config.ts", "src/**/*.ts"] +} diff --git a/apps/url/client-e2e/.eslintrc.json b/apps/url/client-e2e/.eslintrc.json new file mode 100644 index 0000000..3947051 --- /dev/null +++ b/apps/url/client-e2e/.eslintrc.json @@ -0,0 +1,10 @@ +{ + "extends": ["plugin:cypress/recommended", "../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/apps/url/client-e2e/cypress.config.ts b/apps/url/client-e2e/cypress.config.ts new file mode 100644 index 0000000..a45b4fd --- /dev/null +++ b/apps/url/client-e2e/cypress.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from 'cypress'; +import { nxE2EPreset } from '@nx/cypress/plugins/cypress-preset'; + +export default defineConfig({ + e2e: nxE2EPreset(__dirname, { + bundler: 'vite', + }), +}); diff --git a/apps/url/client-e2e/project.json b/apps/url/client-e2e/project.json new file mode 100644 index 0000000..2468499 --- /dev/null +++ b/apps/url/client-e2e/project.json @@ -0,0 +1,33 @@ +{ + "name": "url-client-e2e", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "apps/url/client-e2e/src", + "projectType": "application", + "targets": { + "e2e": { + "executor": "@nx/cypress:cypress", + "options": { + "cypressConfig": "apps/url/client-e2e/cypress.config.ts", + "devServerTarget": "url-client:serve:development", + "testingType": "e2e" + }, + "configurations": { + "production": { + "devServerTarget": "url-client:serve:production" + }, + "ci": { + "devServerTarget": "url-client:serve-static" + } + } + }, + "lint": { + "executor": "@nx/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["apps/url/client-e2e/**/*.{js,ts}"] + } + } + }, + "tags": [], + "implicitDependencies": ["url-client"] +} diff --git a/apps/url/client-e2e/src/e2e/app.cy.ts b/apps/url/client-e2e/src/e2e/app.cy.ts new file mode 100644 index 0000000..adb8aad --- /dev/null +++ b/apps/url/client-e2e/src/e2e/app.cy.ts @@ -0,0 +1,13 @@ +import { getGreeting } from '../support/app.po'; + +describe('url-client', () => { + beforeEach(() => cy.visit('/')); + + it('should display welcome message', () => { + // Custom command example, see `../support/commands.ts` file + cy.login('my-email@something.com', 'myPassword'); + + // Function helper example, see `../support/app.po.ts` file + getGreeting().contains('Welcome url-client'); + }); +}); diff --git a/apps/url/client-e2e/src/fixtures/example.json b/apps/url/client-e2e/src/fixtures/example.json new file mode 100644 index 0000000..294cbed --- /dev/null +++ b/apps/url/client-e2e/src/fixtures/example.json @@ -0,0 +1,4 @@ +{ + "name": "Using fixtures to represent data", + "email": "hello@cypress.io" +} diff --git a/apps/url/client-e2e/src/support/app.po.ts b/apps/url/client-e2e/src/support/app.po.ts new file mode 100644 index 0000000..3293424 --- /dev/null +++ b/apps/url/client-e2e/src/support/app.po.ts @@ -0,0 +1 @@ +export const getGreeting = () => cy.get('h1'); diff --git a/apps/url/client-e2e/src/support/commands.ts b/apps/url/client-e2e/src/support/commands.ts new file mode 100644 index 0000000..310f1fa --- /dev/null +++ b/apps/url/client-e2e/src/support/commands.ts @@ -0,0 +1,33 @@ +// *********************************************** +// This example commands.js shows you how to +// create various custom commands and overwrite +// existing commands. +// +// For more comprehensive examples of custom +// commands please read more here: +// https://on.cypress.io/custom-commands +// *********************************************** + +// eslint-disable-next-line @typescript-eslint/no-namespace +declare namespace Cypress { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + interface Chainable { + login(email: string, password: string): void; + } +} +// +// -- This is a parent command -- +Cypress.Commands.add('login', (email, password) => { + console.log('Custom command example: Login', email, password); +}); +// +// -- This is a child command -- +// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) +// +// +// -- This is a dual command -- +// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) +// +// +// -- This will overwrite an existing command -- +// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) diff --git a/apps/url/client-e2e/src/support/e2e.ts b/apps/url/client-e2e/src/support/e2e.ts new file mode 100644 index 0000000..3d469a6 --- /dev/null +++ b/apps/url/client-e2e/src/support/e2e.ts @@ -0,0 +1,17 @@ +// *********************************************************** +// This example support/index.js is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.js using ES2015 syntax: +import './commands'; diff --git a/apps/url/client-e2e/tsconfig.json b/apps/url/client-e2e/tsconfig.json new file mode 100644 index 0000000..4791529 --- /dev/null +++ b/apps/url/client-e2e/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "sourceMap": false, + "outDir": "../../../dist/out-tsc", + "allowJs": true, + "types": ["cypress", "node"] + }, + "include": ["src/**/*.ts", "src/**/*.js", "cypress.config.ts"] +} diff --git a/apps/url/client/.eslintrc.json b/apps/url/client/.eslintrc.json new file mode 100644 index 0000000..75b8507 --- /dev/null +++ b/apps/url/client/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["plugin:@nx/react", "../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/apps/url/client/index.html b/apps/url/client/index.html new file mode 100644 index 0000000..b31b8f2 --- /dev/null +++ b/apps/url/client/index.html @@ -0,0 +1,16 @@ + + + + + UrlClient + + + + + + + +
+ + + diff --git a/apps/url/client/project.json b/apps/url/client/project.json new file mode 100644 index 0000000..0a6c869 --- /dev/null +++ b/apps/url/client/project.json @@ -0,0 +1,78 @@ +{ + "name": "url-client", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "apps/url/client/src", + "projectType": "application", + "targets": { + "build": { + "executor": "@nx/vite:build", + "outputs": ["{options.outputPath}"], + "defaultConfiguration": "production", + "options": { + "outputPath": "dist/apps/url/client" + }, + "configurations": { + "development": { + "mode": "development" + }, + "production": { + "mode": "production" + } + } + }, + "serve": { + "executor": "@nx/vite:dev-server", + "defaultConfiguration": "development", + "options": { + "buildTarget": "url-client:build" + }, + "configurations": { + "development": { + "buildTarget": "url-client:build:development", + "hmr": true + }, + "production": { + "buildTarget": "url-client:build:production", + "hmr": false + } + } + }, + "preview": { + "executor": "@nx/vite:preview-server", + "defaultConfiguration": "development", + "options": { + "buildTarget": "url-client:build" + }, + "configurations": { + "development": { + "buildTarget": "url-client:build:development" + }, + "production": { + "buildTarget": "url-client:build:production" + } + } + }, + "test": { + "executor": "@nx/vite:test", + "outputs": ["coverage/apps/url/client"], + "options": { + "passWithNoTests": true, + "reportsDirectory": "../../../coverage/apps/url/client" + } + }, + "lint": { + "executor": "@nx/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["apps/url/client/**/*.{ts,tsx,js,jsx}"] + } + }, + "serve-static": { + "executor": "@nx/web:file-server", + "options": { + "buildTarget": "url-client:build" + } + } + }, + "tags": [] +} diff --git a/apps/url/client/public/favicon.ico b/apps/url/client/public/favicon.ico new file mode 100644 index 0000000..317ebcb Binary files /dev/null and b/apps/url/client/public/favicon.ico differ diff --git a/apps/url/client/src/app/app.module.css b/apps/url/client/src/app/app.module.css new file mode 100644 index 0000000..7b88fba --- /dev/null +++ b/apps/url/client/src/app/app.module.css @@ -0,0 +1 @@ +/* Your styles goes here. */ diff --git a/apps/url/client/src/app/app.spec.tsx b/apps/url/client/src/app/app.spec.tsx new file mode 100644 index 0000000..a472e10 --- /dev/null +++ b/apps/url/client/src/app/app.spec.tsx @@ -0,0 +1,25 @@ +import { render } from '@testing-library/react'; + +import { BrowserRouter } from 'react-router-dom'; + +import App from './app'; + +describe('App', () => { + it('should render successfully', () => { + const { baseElement } = render( + + + + ); + expect(baseElement).toBeTruthy(); + }); + + it('should have a greeting as the title', () => { + const { getByText } = render( + + + + ); + expect(getByText(/Welcome url-client/gi)).toBeTruthy(); + }); +}); diff --git a/apps/url/client/src/app/app.tsx b/apps/url/client/src/app/app.tsx new file mode 100644 index 0000000..9f1a8f4 --- /dev/null +++ b/apps/url/client/src/app/app.tsx @@ -0,0 +1,47 @@ +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { FormEvent, useCallback, useState } from 'react'; +import styles from './app.module.css'; + +import NxWelcome from './nx-welcome'; + +import { Route, Routes, Link } from 'react-router-dom'; + +type Shortened = { + original: string; + short: string; +}; + +export function App() { + const [inputUrl, setInputUrl] = useState(''); + + const onSubmit = useCallback( + (event: FormEvent) => { + event.preventDefault(); + console.log(event); + }, + [] + ); + + return ( +
+

My URL Shortener

+

{inputUrl}

+
+ + { + setInputUrl(e.target.value); + }} + placeholder="www.my-super-long-url-here.com/12345" + />fuse + +
+
    +
  • ex.co/abcde - www.example.com/this/is/a/long/slug
  • +
+
+ ); +} + +export default App; diff --git a/apps/url/client/src/app/nx-welcome.tsx b/apps/url/client/src/app/nx-welcome.tsx new file mode 100644 index 0000000..f0cd657 --- /dev/null +++ b/apps/url/client/src/app/nx-welcome.tsx @@ -0,0 +1,845 @@ +/* + * * * * * * * * * * * * * * * * * * * * * * * * * * * * + This is a starter component and can be deleted. + * * * * * * * * * * * * * * * * * * * * * * * * * * * * + Delete this file and get started with your project! + * * * * * * * * * * * * * * * * * * * * * * * * * * * * + */ +export function NxWelcome({ title }: { title: string }) { + return ( + <> +