diff --git a/.ansible-items/inventory b/.ansible-items/inventory index 32090d7..5301913 100644 --- a/.ansible-items/inventory +++ b/.ansible-items/inventory @@ -1,2 +1,11 @@ -[host] -{{ DEPLOYMENT_USER }}@{{ DEPLOY_HOSTNAME }} ansible_connection=ssh ansible_user={{ DEPLOYMENT_USER }} \ No newline at end of file +all: + children: + sptarkov: + hosts: + sp-tarkov: + ansible_host: {{ DEPLOY_HOSTNAME }} + ansible_user: {{ DEPLOY_USER }} + ansible_connection: paramiko_ssh + ansible_ssh_pass: {{ DEPLOY_SSH_KEY_PASSPHRASE }} + ansible_ssh_user: {{ DEPLOY_USER }} + ansible_ssh_private_key_file: /drone/src/private.key # Hardcoded because created by the Drone plugin \ No newline at end of file diff --git a/.ansible-items/playbook.yml b/.ansible-items/playbook.yml index dea7313..61212b4 100644 --- a/.ansible-items/playbook.yml +++ b/.ansible-items/playbook.yml @@ -1,6 +1,6 @@ --- - name: install and configure PHP8 and composer - hosts: host + hosts: sptarkov tasks: - name: Delete spt-items-api before adding everything again @@ -36,9 +36,9 @@ - name: Reset files permissions file: path: "{{ lookup('env', 'SPT_ITEMS_PATH') }}" - owner: "{{ lookup('env', 'DEPLOYMENT_USER') }}" - group: "{{ lookup('env', 'DEPLOYMENT_USER_GROUP') }}" - mode: 0774 + owner: "{{ lookup('env', 'DEPLOY_USER') }}" + group: "{{ lookup('env', 'DEPLOY_USER_GROUP') }}" + mode: 0775 recurse: yes - name: Initialize database diff --git a/.drone-docker.yml b/.drone-docker.yml index 4785f9e..130dc28 100644 --- a/.drone-docker.yml +++ b/.drone-docker.yml @@ -8,66 +8,96 @@ concurrency: trigger: event: - push - branch: - - master - - main - - development steps: -- name: replace hosts and user variables - image: ubuntu:impish - environment: - DEPLOY_HOSTNAME: - from_secret: deploy_hostname - SPT_ITEMS_HOSTNAME: - from_secret: spt_items_hostname - DEPLOYMENT_USER: - from_secret: deploy_username - commands: - - sed -i 's/{{ SPT_ITEMS_HOSTNAME }}/'"$SPT_ITEMS_HOSTNAME"'/g' ./items/frontend/.env.example - - mv ./items/frontend/.env.example ./items/frontend/.env - - sed -i 's/{{ DEPLOY_HOSTNAME }}/'"$DEPLOY_HOSTNAME"'/g' ./.ansible-items/inventory - - sed -i 's/{{ DEPLOYMENT_USER }}/'"$DEPLOYMENT_USER"'/g' ./.ansible-items/inventory + - name: replace hosts and user variables + image: ubuntu:impish + environment: + SPT_ITEMS_HOSTNAME: + from_secret: spt_items_hostname + DEPLOY_HOSTNAME: + from_secret: deploy_hostname + DEPLOY_USER: + from_secret: deploy_username + DEPLOY_PRIVATE_KEY: + from_secret: deploy_ssh_key + DEPLOY_SSH_KEY_PASSPHRASE: + from_secret: deploy_ssh_key_passphrase + commands: + - sed -i 's/{{ SPT_ITEMS_HOSTNAME }}/'"$SPT_ITEMS_HOSTNAME"'/g' ./items/frontend/.env.example + - mv ./items/frontend/.env.example ./items/frontend/.env + - echo "$DEPLOY_PRIVATE_KEY" > private.key && chmod 600 private.key + - sed -i 's/{{ DEPLOY_HOSTNAME }}/'"$DEPLOY_HOSTNAME"'/g' ./.ansible-items/inventory + - sed -i 's/{{ DEPLOY_SSH_KEY_PASSPHRASE }}/'"$DEPLOY_SSH_KEY_PASSPHRASE"'/g' ./.ansible-items/inventory + - sed -i 's/{{ DEPLOY_USER }}/'"$DEPLOY_USER"'/g' ./.ansible-items/inventory -- name: build frontend - image: node:lts-alpine3.14 - commands: - - node -v - - npm -v - - yarn --version - - yarn --cwd ./items/frontend install - - yarn --cwd ./items/frontend build --pure-lockfile - - rm -rf ./items/api/public/static/* - - mv ./items/frontend/build/* ./items/api/public - - rm ./items/api/public/index.html + - name: install dependencies and build frontend + image: node:lts-alpine3.14 + commands: + - node -v + - npm -v + - yarn --version + - yarn --cwd ./items/frontend install + - yarn --cwd ./items/frontend build --pure-lockfile + - rm -rf ./items/api/public/static/* + - cp -r ./items/frontend/build/* ./items/api/public + - rm ./items/api/public/index.html + depends_on: + - replace hosts and user variables -- name: check ansible syntax - image: plugins/ansible:3 - settings: - playbook: ./.ansible-items/playbook.yml - inventory: ./.ansible-items/inventory - galaxy: ./.ansible-items/requirements.yml - syntax_check: true -- name: apply ansible playbook - image: plugins/ansible:3 - settings: - playbook: ./.ansible-items/playbook.yml - inventory: ./.ansible-items/inventory - galaxy: ./.ansible-items/requirements.yml - private_key: - from_secret: deploy_ssh_key - environment: - DEPLOY_HOSTNAME: - from_secret: deploy_hostname - SPT_ITEMS_HOSTNAME: - from_secret: spt_items_hostname - DEPLOYMENT_USER: - from_secret: deploy_username - DEPLOYMENT_USER_GROUP: - from_secret: deploy_user_group - SPT_ITEMS_PATH: - from_secret: deploy_path - when: - branch: - - master - - main \ No newline at end of file + - name: frontend + image: nginx:1.21.4-alpine + commands: + - cp -r ./items/frontend/build/* /usr/share/nginx/html + - cp ./items/frontend/src/cypress/nginx_config/default.conf /etc/nginx/conf.d/default.conf + - nginx -g "daemon off;" + detach: true + depends_on: + - install dependencies and build frontend + + - name: test frontend + image: cypress/browsers:node16.5.0-chrome94-ff93 + commands: + - node -v + - npm -v + - yarn --version + - yarn --cwd ./items/frontend cy:ci + depends_on: + - install dependencies and build frontend + + - name: check ansible syntax + image: plugins/ansible:3 + settings: + playbook: ./.ansible-items/playbook.yml + inventory: ./.ansible-items/inventory + galaxy: ./.ansible-items/requirements.yml + syntax_check: true + when: + branch: + - development + + - name: apply ansible playbook + image: plugins/ansible:3 + settings: + playbook: ./.ansible-items/playbook.yml + inventory: ./.ansible-items/inventory + galaxy: ./.ansible-items/requirements.yml + timeout: 60 + verbose: 2 + environment: + SPT_ITEMS_HOSTNAME: + from_secret: spt_items_hostname + DEPLOY_HOSTNAME: + from_secret: deploy_hostname + DEPLOY_USER: + from_secret: deploy_username + DEPLOY_USER_GROUP: + from_secret: deploy_user_group + SPT_ITEMS_PATH: + from_secret: deploy_path + depends_on: + - test frontend + when: + branch: + - master + - main \ No newline at end of file diff --git a/.drone-kubernetes.yml b/.drone-kubernetes.yml index 3d09772..f0c8d9d 100644 --- a/.drone-kubernetes.yml +++ b/.drone-kubernetes.yml @@ -8,66 +8,96 @@ concurrency: trigger: event: - push - branch: - - master - - main - - development steps: -- name: replace hosts and user variables - image: ubuntu:impish - environment: - DEPLOY_HOSTNAME: - from_secret: deploy_hostname - SPT_ITEMS_HOSTNAME: - from_secret: spt_items_hostname - DEPLOYMENT_USER: - from_secret: deploy_username - commands: - - sed -i 's/{{ SPT_ITEMS_HOSTNAME }}/'"$SPT_ITEMS_HOSTNAME"'/g' ./items/frontend/.env.example - - mv ./items/frontend/.env.example ./items/frontend/.env - - sed -i 's/{{ DEPLOY_HOSTNAME }}/'"$DEPLOY_HOSTNAME"'/g' ./.ansible-items/inventory - - sed -i 's/{{ DEPLOYMENT_USER }}/'"$DEPLOYMENT_USER"'/g' ./.ansible-items/inventory + - name: replace hosts and user variables + image: ubuntu:impish + environment: + SPT_ITEMS_HOSTNAME: + from_secret: spt_items_hostname + DEPLOY_HOSTNAME: + from_secret: deploy_hostname + DEPLOY_USER: + from_secret: deploy_username + DEPLOY_PRIVATE_KEY: + from_secret: deploy_ssh_key + DEPLOY_SSH_KEY_PASSPHRASE: + from_secret: deploy_ssh_key_passphrase + commands: + - sed -i 's/{{ SPT_ITEMS_HOSTNAME }}/'"$SPT_ITEMS_HOSTNAME"'/g' ./items/frontend/.env.example + - mv ./items/frontend/.env.example ./items/frontend/.env + - echo "$DEPLOY_PRIVATE_KEY" > private.key && chmod 600 private.key + - sed -i 's/{{ DEPLOY_HOSTNAME }}/'"$DEPLOY_HOSTNAME"'/g' ./.ansible-items/inventory + - sed -i 's/{{ DEPLOY_SSH_KEY_PASSPHRASE }}/'"$DEPLOY_SSH_KEY_PASSPHRASE"'/g' ./.ansible-items/inventory + - sed -i 's/{{ DEPLOY_USER }}/'"$DEPLOY_USER"'/g' ./.ansible-items/inventory -- name: build frontend - image: node:lts-alpine3.14 - commands: - - node -v - - npm -v - - yarn --version - - yarn --cwd ./items/frontend install - - yarn --cwd ./items/frontend build --pure-lockfile - - rm -rf ./items/api/public/static/* - - mv ./items/frontend/build/* ./items/api/public - - rm ./items/api/public/index.html + - name: install dependencies and build frontend + image: node:lts-alpine3.14 + commands: + - node -v + - npm -v + - yarn --version + - yarn --cwd ./items/frontend install + - yarn --cwd ./items/frontend build --pure-lockfile + - rm -rf ./items/api/public/static/* + - cp -r ./items/frontend/build/* ./items/api/public + - rm ./items/api/public/index.html + depends_on: + - replace hosts and user variables -- name: check ansible syntax - image: plugins/ansible:3 - settings: - playbook: ./.ansible-items/playbook.yml - inventory: ./.ansible-items/inventory - galaxy: ./.ansible-items/requirements.yml - syntax_check: true -- name: apply ansible playbook - image: plugins/ansible:3 - settings: - playbook: ./.ansible-items/playbook.yml - inventory: ./.ansible-items/inventory - galaxy: ./.ansible-items/requirements.yml - private_key: - from_secret: deploy_ssh_key - environment: - DEPLOY_HOSTNAME: - from_secret: deploy_hostname - SPT_ITEMS_HOSTNAME: - from_secret: spt_items_hostname - DEPLOYMENT_USER: - from_secret: deploy_username - DEPLOYMENT_USER_GROUP: - from_secret: deploy_user_group - SPT_ITEMS_PATH: - from_secret: deploy_path - when: - branch: - - master - - main \ No newline at end of file + - name: frontend + image: nginx:1.21.4-alpine + commands: + - cp -r ./items/frontend/build/* /usr/share/nginx/html + - cp ./items/frontend/src/cypress/nginx_config/default.conf /etc/nginx/conf.d/default.conf + - nginx -g "daemon off;" + detach: true + depends_on: + - install dependencies and build frontend + + - name: test frontend + image: cypress/browsers:node16.5.0-chrome94-ff93 + commands: + - node -v + - npm -v + - yarn --version + - yarn --cwd ./items/frontend cy:ci + depends_on: + - install dependencies and build frontend + + - name: check ansible syntax + image: plugins/ansible:3 + settings: + playbook: ./.ansible-items/playbook.yml + inventory: ./.ansible-items/inventory + galaxy: ./.ansible-items/requirements.yml + syntax_check: true + when: + branch: + - development + + - name: apply ansible playbook + image: plugins/ansible:3 + settings: + playbook: ./.ansible-items/playbook.yml + inventory: ./.ansible-items/inventory + galaxy: ./.ansible-items/requirements.yml + timeout: 60 + verbose: 2 + environment: + SPT_ITEMS_HOSTNAME: + from_secret: spt_items_hostname + DEPLOY_HOSTNAME: + from_secret: deploy_hostname + DEPLOY_USER: + from_secret: deploy_username + DEPLOY_USER_GROUP: + from_secret: deploy_user_group + SPT_ITEMS_PATH: + from_secret: deploy_path + depends_on: + - test frontend + when: + branch: + - master + - main \ No newline at end of file diff --git a/items/README.md b/items/README.md index 9b22418..7780074 100644 --- a/items/README.md +++ b/items/README.md @@ -14,6 +14,7 @@ | deploy_username | The default username to use on the remote server
used by Ansible SSH | www-data | | deploy_user_group | The default user group to use on the remote server
used to set permission on the website folder | www-data | | deploy_ssh_key | The **content** of the ssh private key used to connect to the remote server
The key needs to be in RSA in "RSA PRIVATE KEY" format
The ssh publick key needs to already be in the user used in the remote server ~/.ssh/authorized_keys | -----BEGIN RSA PRIVATE KEY-----
The key
-----END RSA PRIVATE KEY----- | +| deploy_ssh_key_passphrase | The passphrase to decrypt the SSH private key | test | ## ⚠ Important notes for the deployment ⚠ * Add all required secrets in Drone @@ -31,9 +32,12 @@ ## The pipeline summary 1. Each push will: - 1. Builds the frontend - 1. Move the build frontend in the backend `public` folder -1. IF Promoted to production, deploys to the server + 1. Test the frontend + 2. IF on `development` or `master`/`main` branch + 1. Builds the frontend + 3. IF pushed from `master` or `main` main branch + 1. Move the build frontend in the backend `public` folder + 2. Deploys to the server ## The pipeline walkthrough see [Walkthrough.md](./docs/Walkthrough.md) diff --git a/items/docs/Walkthrough.md b/items/docs/Walkthrough.md index ce56bac..b3e9094 100644 --- a/items/docs/Walkthrough.md +++ b/items/docs/Walkthrough.md @@ -4,12 +4,12 @@ * [Overview](#overview) * [Pipeline definition](#pipeline-definition) * [Pipeline concurrency](#pipeline-concurrency) -* [Environment variables](#environment-variables) * [Triggers](#triggers) * [Steps](#steps) - * [Fetch and update submodules](#Fetch-and-update-submodules) * [Replace hosts and user variables](#replace-hosts-and-user-variables) - * [Build frontend](#build-frontend) + * [Install frontend dependencies and build it](#install-frontend-dependencies-and-build-it) + * [Run the frontend](#run-the-frontend) + * [Test frontend](#test-frontend) * [Check ansible syntax](#check-ansible-syntax) * [Apply ansible playbook](#apply-ansible-playbook) * [Playbook definition](#playbook-definition) @@ -17,8 +17,8 @@ * [Copy the project](#copy-the-project) * [Copy PHP env file](#copy-php-env-file) * [Get JavaScript chunks name](#get-javascript-chunks-name) - * [Get file names from find output](#get-file-from-find-output) - * [Copy app.blade.php file](#copy-app-blade-php-file) + * [Get file names from find output](#get-file-names-from-find-output) + * [Copy app.blade.php file](#copy-appbladephp-file) * [Download and install composer dependencies](#download-and-install-composer-dependencies) ## Overview @@ -26,10 +26,10 @@ * the backend is a [submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules) located in [api](../api) that points towards [https://dev.sp-tarkov.com/Rev/spt-items-api.git](https://dev.sp-tarkov.com/Rev/spt-items-api.git) * the frontend is a [submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules) located in [frontend](../frontend) that points towards [https://dev.sp-tarkov.com/shirito/item-finder-website-frontend.git](https://dev.sp-tarkov.com/shirito/item-finder-website-frontend.git) * There are two Ansible pipelines - * A docker pipeline [drone-docker.yml](../drone-docker.yml) - * A kubernetes pipeline [drone-kubernetes.yml](../drone-kubernetes.yml) -* All ansible playbook files are located in [.ansible](../.ansible) -* The documentation is located in [documentation](../documentation) + * A docker pipeline [drone-docker.yml](../../.drone-docker.yml) + * A kubernetes pipeline [drone-kubernetes.yml](../../.drone-kubernetes.yml) +* All ansible playbook files are located in [.ansible](../../.ansible-items) +* The documentation is located in [documentation](../docs) ## Pipeline definition ```yml @@ -46,54 +46,50 @@ concurrency: ``` The pipeline is set to only one build at a time (every subsequent build with be pending). -## Environment variables -```yml -environment: - SPT_ITEMS_PATH: /var/www/html/aki/spt-items-api -``` -Here are the environment variables. They are automatically injected in every step. - ## Triggers ```yml trigger: event: - push - branch: - - master - - main - - development ``` -The pipeline is run on every push only on branches `master`, `main` and `development`. We want to check that every development on `development` branch is correct and deploy automatically when merged in `master`/`main`. +The pipeline is run on every push We want to check that every development on `development` branch is correct and deploy automatically when merged in `master`/`main`. ## Steps ### Replace hosts and user variables ```yml - name: replace hosts and user variables image: ubuntu:impish environment: - DEPLOY_HOSTNAME: - from_secret: deploy_hostname SPT_ITEMS_HOSTNAME: from_secret: spt_items_hostname - DEPLOYMENT_USER: + DEPLOY_HOSTNAME: + from_secret: deploy_hostname + DEPLOY_USER: from_secret: deploy_username + DEPLOY_PRIVATE_KEY: + from_secret: deploy_ssh_key + DEPLOY_SSH_KEY_PASSPHRASE: + from_secret: deploy_ssh_key_passphrase commands: - - sed -i 's/{{ SPT_ITEMS_HOSTNAME }}/'"$SPT_ITEMS_HOSTNAME"'/g' ./items/frontend/.env.example - - mv ./items/frontend/.env.example ./items/frontend/.env - - sed -i 's/{{ DEPLOY_HOSTNAME }}/'"$DEPLOY_HOSTNAME"'/g' ./.ansible-items/inventory - - sed -i 's/{{ DEPLOYMENT_USER }}/'"$DEPLOYMENT_USER"'/g' ./.ansible-items/inventory + - sed -i 's/{{ SPT_ITEMS_HOSTNAME }}/'"$SPT_ITEMS_HOSTNAME"'/g' ./items/frontend/.env.example + - mv ./items/frontend/.env.example ./items/frontend/.env + - echo "$DEPLOY_PRIVATE_KEY" > private.key && chmod 600 private.key + - sed -i 's/{{ DEPLOY_HOSTNAME }}/'"$DEPLOY_HOSTNAME"'/g' ./.ansible-items/inventory + - sed -i 's/{{ DEPLOY_SSH_KEY_PASSPHRASE }}/'"$DEPLOY_SSH_KEY_PASSPHRASE"'/g' ./.ansible-items/inventory + - sed -i 's/{{ DEPLOY_USER }}/'"$DEPLOY_USER"'/g' ./.ansible-items/inventory ``` Executed on every push. \ The following environment variables are injected using Drone secrets: * `SPT_ITEMS_HOSTNAME` is used by the frontend to call the backend. * `DEPLOY_HOSTNAME` is used by Ansible to connect to the remote server via SSH. - * `DEPLOYMENT_USER` is used by Ansible to connect to the remote server via SSH. - * all environment variables at the pipeline level (see [Environment variables](#environment-variables)) + * `DEPLOY_USER` is used by Ansible to connect to the remote server via SSH. + * `DEPLOY_PRIVATE_KEY` is the SSH key used to connect to the remote server via SSH + * `DEPLOY_SSH_KEY_PASSPHRASE` is the SSH key passphrase Using `sed` makes temporary changes in the container/pod instead of commiting secrets in the repo in plain text. \ The changes are never pushed and are discarded when the container/pod is terminated. -### Build frontend +### Install frontend dependencies and build it ```yml -- name: build frontend +- name: install dependencies and build frontend image: node:lts-alpine3.14 commands: - node -v @@ -102,15 +98,46 @@ The changes are never pushed and are discarded when the container/pod is termina - yarn --cwd ./items/frontend install - yarn --cwd ./items/frontend build --pure-lockfile - rm -rf ./items/api/public/static/* - - mv ./items/frontend/build/* ./items/api/public + - cp -r ./items/frontend/build/* ./items/api/public - rm ./items/api/public/index.html + depends_on: + - replace hosts and user variables ``` Executed on every push. \ Since the PHP backend serves the ReactJS frontend, the former is built and moved in the latter. Notes: - * `yarn --cwd ` executes the command in the specified file - * `rm -rf ./api/public/static/*` deletes the static files to make sure no old JavaScript files remain - * `rm ./api/public/index.html` ReactJS is bundled with a `index.html`. It is discarded to use [](https://dev.sp-tarkov.com/Rev/spt-items-api/raw/branch/master/resources/views/app.blade.php) instead. +* `yarn --cwd ` executes the command in the specified file +* `rm -rf ./api/public/static/*` deletes the static files to make sure no old JavaScript files remain +* `rm ./api/public/invdex.html` ReactJS is bundled with a `index.html`. It is discarded to use [](https://dev.sp-tarkov.com/Rev/spt-items-api/raw/branch/master/resources/views/app.blade.php) instead. + +### Run the frontend +```yaml +- name: frontend + image: nginx:1.21.4-alpine + commands: + - cp -r ./items/frontend/build/* /usr/share/nginx/html + - cp ./items/frontend/src/cypress/nginx_config/default.conf /etc/nginx/conf.d/default.conf + - nginx -g "daemon off;" + detach: true + depends_on: + - install dependencies and build frontend +``` +Copies the frontend and the nginx conf in the container to be able to test it. +The frontend is run and `detach` is specified so the End-to-End tests (using cypress) can run on it + +### Test frontend +```yaml +- name: test frontend + image: cypress/browsers:node16.5.0-chrome94-ff93 + commands: + - node -v + - npm -v + - yarn --version + - yarn --cwd ./items/frontend cy:ci + depends_on: + - install dependencies and build frontend +``` +Run frontend tests using Cypress ### Check ansible syntax ```yml @@ -121,9 +148,12 @@ Notes: inventory: ./.ansible-items/inventory galaxy: ./.ansible-items/requirements.yml syntax_check: true + when: + branch: + - development ``` Executed on every push. \ -Check the Ansible syntax in [playbook.yml](../.ansible/playbook.yml), [inventory](../.ansible/inventory) and [requirements.yml](../.ansible/requirements.yml). The check is executed on every push since we want to detect any error before validating the build using the promotion. +Check the Ansible syntax in [playbook.yml](../../.ansible-items/playbook.yml), [inventory](../../.ansible-items/inventory) and [requirements.yml](../../.ansible-items/requirements.yml). The check is executed on every push since we want to detect any error before validating the build using the promotion. ### Apply ansible playbook ```yml @@ -133,15 +163,21 @@ Check the Ansible syntax in [playbook.yml](../.ansible/playbook.yml), [inventory playbook: ./.ansible-items/playbook.yml inventory: ./.ansible-items/inventory galaxy: ./.ansible-items/requirements.yml - private_key: - from_secret: deploy_ssh_key + timeout: 60 + verbose: 2 environment: - DEPLOY_HOSTNAME: - from_secret: deploy_hostname SPT_ITEMS_HOSTNAME: from_secret: spt_items_hostname - DEPLOYMENT_USER: + DEPLOY_HOSTNAME: + from_secret: deploy_hostname + DEPLOY_USER: from_secret: deploy_username + DEPLOY_USER_GROUP: + from_secret: deploy_user_group + SPT_ITEMS_PATH: + from_secret: deploy_path + depends_on: + - test frontend when: branch: - master @@ -153,14 +189,15 @@ This step is [idempotent](https://en.wikipedia.org/wiki/Idempotence). \ The following environment variables are injected using Drone secrets: * `SPT_ITEMS_HOSTNAME` is used by the PHP env file. * `DEPLOY_HOSTNAME` is used to connect to the remote server via SSH. - * `DEPLOYMENT_USER` is used to connect to the remote server via SSH. - * all environment variables at the pipeline level (see [Environment variables](#environment-variables)) + * `DEPLOY_USER` is used to connect to the remote server via SSH. + * `DEPLOY_USER_GROUP` is the user group, used to give read/write/execute permissions to the whole group. It must be the same as Nginx's user. + * `SPT_ITEMS_PATH` is the path on the remote server where the files will be copyed to. #### Playbook definition ```yml -hosts: host +hosts: sptarkov ``` -Uses the host defined in [inventory](../.ansible/inventory). Remember, the step [Replace hosts and user variables](#replace-hosts-and-user-variables) already replaced the variables at this point. The playbook will be executed as `root` user using `sudo`. +Uses the host defined in [inventory](../../.ansible-items/inventory). Remember, the step [Replace hosts and user variables](#replace-hosts-and-user-variables) already replaced the variables at this point. #### Delete old spt-items-api ```yml @@ -170,7 +207,7 @@ Uses the host defined in [inventory](../.ansible/inventory). Remember, the step path: "{{ lookup('env', 'SPT_ITEMS_PATH') }}" ``` Since the copy does not override the folder, this step takes care of it. \ -`SPT_ITEMS_PATH` is injected thanks to the pipeline level environment variables (see [Environment variables](#environment-variables)) +`SPT_ITEMS_PATH` is injected in the environments properties (see [Apply ansible playbook](#apply-ansible-playbook)) #### Copy the project ```yml @@ -188,8 +225,8 @@ Copies the whole project (frontend and backend) from the [api](../api) folder in src: ./templates/.php-env.j2 dest: "{{ lookup('env', 'SPT_ITEMS_PATH') }}/.env" ``` -Uses [Jinja2](https://jinja2docs.readthedocs.io/en/stable/) to resolve the [template for the PHP .env file](../.ansible/templates/.php_env.j2). \ -`SPT_ITEMS_PATH` is injected thanks to the pipeline level environment variables (see [Environment variables](#environment-variables)). \ +Uses [Jinja2](https://jinja2docs.readthedocs.io/en/stable/) to resolve the [template for the PHP .env file](../../.ansible-items/templates/.php-env.j2). \ +`SPT_ITEMS_PATH` is injected in the environments properties (see [Apply ansible playbook](#apply-ansible-playbook)) \ `SPT_ITEMS_HOSTNAME` is injected in the environments properties (see [Apply ansible playbook](#apply-ansible-playbook)) #### Get JavaScript chunks name @@ -199,8 +236,8 @@ Uses [Jinja2](https://jinja2docs.readthedocs.io/en/stable/) to resolve the [temp cmd: find "{{ lookup('env', 'SPT_ITEMS_PATH') }}" -type f -name "*chunk.js" -printf "%f\n" register: find_output ``` -Prepare a find of all JavaScript chunk files for the [app.blade.php.j2](../.ansible/templates/app.blade.php.j2) template. \ -`SPT_ITEMS_PATH` is injected thanks to the pipeline level environment variables (see [Environment variables](#environment-variables)). +Prepare a find of all JavaScript chunk files for the [app.blade.php.j2](../../.ansible-items/templates/app.blade.php.j2) template. \ +`SPT_ITEMS_PATH` is injected in the environments properties (see [Apply ansible playbook](#apply-ansible-playbook)) #### Get file names from find output ```yml @@ -208,7 +245,7 @@ Prepare a find of all JavaScript chunk files for the [app.blade.php.j2](../.ansi set_fact: chunk_list: "{{ find_output['stdout'].split('\n') }}" ``` -Splits the string containing the list of all JavaScript chunk files for the [app.blade.php.j2](../.ansible/templates/app.blade.php.j2) template. +Splits the string containing the list of all JavaScript chunk files for the [app.blade.php.j2](../../.ansible-items/templates/app.blade.php.j2) template. #### Copy app.blade.php file ```yml @@ -217,8 +254,8 @@ Splits the string containing the list of all JavaScript chunk files for the [app src: ./templates/app.blade.php.j2 dest: "{{ lookup('env', 'SPT_ITEMS_PATH') }}/resources/views/app.blade.php" ``` -Uses [Jinja2](https://jinja2docs.readthedocs.io/en/stable/) to resolve the [template for the PHP app.blade.php file](../.ansible/templates/app.blade.php.j2). \ -`SPT_ITEMS_PATH` is injected thanks to the pipeline level environment variables (see [Environment variables](#environment-variables)). +Uses [Jinja2](https://jinja2docs.readthedocs.io/en/stable/) to resolve the [template for the PHP app.blade.php file](../../.ansible-items/templates/app.blade.php.j2). \ +`SPT_ITEMS_PATH` is injected in the environments properties (see [Apply ansible playbook](#apply-ansible-playbook)). #### Download and install composer dependencies ```yml @@ -233,17 +270,16 @@ Uses [Jinja2](https://jinja2docs.readthedocs.io/en/stable/) to resolve the [temp - name: Reset files permissions file: path: "{{ lookup('env', 'SPT_ITEMS_PATH') }}" - owner: "{{ lookup('env', 'DEPLOYMENT_USER') }}" - group: www-data + owner: "{{ lookup('env', 'DEPLOY_USER') }}" + group: "{{ lookup('env', 'DEPLOY_USER_GROUP') }}" mode: 0774 recurse: yes ``` Permissions 0644: * user: read/write/execute - * group: read + * group: read/write/execute (for Nginx to execute the php as well as write in the Laravel logs) * other: read -`www-data` is hardcoded here but it should be the standard user for Apache and Nginx. \ -`SPT_ITEMS_PATH` is injected thanks to the pipeline level environment variables (see [Environment variables](#environment-variables)). +`SPT_ITEMS_PATH` is injected in the environments properties (see [Apply ansible playbook](#apply-ansible-playbook)). #### Initialize database ```yml diff --git a/items/frontend/cypress.json b/items/frontend/cypress.json index dcc4426..99278a6 100644 --- a/items/frontend/cypress.json +++ b/items/frontend/cypress.json @@ -1,5 +1,6 @@ { "baseUrl": "http://localhost:3000", + "video": false, "integrationFolder": "src/cypress/integration", "fixtureFolder": "src/cypress/fixtures", "supportFile": "src/cypress/support/index.js", diff --git a/items/frontend/cypress/fixtures/example.json b/items/frontend/cypress/fixtures/example.json deleted file mode 100644 index 02e4254..0000000 --- a/items/frontend/cypress/fixtures/example.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "Using fixtures to represent data", - "email": "hello@cypress.io", - "body": "Fixtures are a great way to mock data for responses to routes" -} diff --git a/items/frontend/package.json b/items/frontend/package.json index 70df6b5..ce25159 100644 --- a/items/frontend/package.json +++ b/items/frontend/package.json @@ -13,8 +13,10 @@ "@types/react": "^17.0.0", "@types/react-dom": "^17.0.0", "cross-env": "^7.0.3", - "cypress": "^8.6.0", - "start-server-and-test": "^1.14.0" + "cypress": "9.0.0", + "start-server-and-test": "^1.14.0", + "typescript": "^4.1.2", + "wait-on": "^6.0.0" }, "dependencies": { "@emotion/react": "^11.4.1", @@ -22,11 +24,12 @@ "@mui/icons-material": "^5.0.3", "@mui/material": "^5.0.3", "@mui/styles": "^5.0.1", + "history": "5", "react": "^17.0.2", "react-dom": "^17.0.2", "react-json-view": "^1.21.3", + "react-router-dom": "6", "react-scripts": "4.0.3", - "typescript": "^4.1.2", "web-vitals": "^1.0.1", "zustand": "^3.5.13" }, @@ -36,8 +39,11 @@ "test:jest": "react-scripts test", "cy:start:app": "cross-env BROWSER=none react-scripts -r @cypress/instrument-cra start", "cy:start:wait": "start-server-and-test cy:start:app http://localhost:3000", + "cy:install": "cypress install", + "cy:verify": "cypress verify", + "cy:run:ci": "cross-env CYPRESS_BASE_URL=http://frontend cypress run --browser chrome", "cy:open": "yarn run cy:start:wait -- \"cypress open\"", - "cy:run": "yarn run cy:start:wait -- \"cypress run\"", + "cy:ci": "yarn run wait-on http-get://frontend && yarn run cy:install && yarn run cy:verify && yarn run cy:run:ci", "eject": "react-scripts eject" }, "eslintConfig": { @@ -59,8 +65,13 @@ ] }, "nyc": { - "include":["src/**/*.ts", "src/**/*.tsx"], - "exclude": ["src/reportWebVitals.ts"], + "include": [ + "src/**/*.ts", + "src/**/*.tsx" + ], + "exclude": [ + "src/reportWebVitals.ts" + ], "excludeAfterRemap": true } } diff --git a/items/frontend/public/favico.ico b/items/frontend/public/favico.ico new file mode 100644 index 0000000..3ff7990 Binary files /dev/null and b/items/frontend/public/favico.ico differ diff --git a/items/frontend/public/favico.svg b/items/frontend/public/favico.svg new file mode 100644 index 0000000..a7f33e5 --- /dev/null +++ b/items/frontend/public/favico.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/items/frontend/public/favicon.ico b/items/frontend/public/favicon.ico deleted file mode 100644 index 66862a5..0000000 Binary files a/items/frontend/public/favicon.ico and /dev/null differ diff --git a/items/frontend/public/favicon.svg b/items/frontend/public/favicon.svg deleted file mode 100644 index e7ee889..0000000 --- a/items/frontend/public/favicon.svg +++ /dev/null @@ -1,109 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/items/frontend/public/index.html b/items/frontend/public/index.html index f439b38..fe4ef5f 100644 --- a/items/frontend/public/index.html +++ b/items/frontend/public/index.html @@ -3,8 +3,8 @@ - - + + @@ -19,7 +19,7 @@ It will be replaced with the URL of the `public` folder during the build. Only files inside the `public` folder can be referenced from the HTML. - Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will + Unlike "/favico.ico" or "favico.ico", "%PUBLIC_URL%/favico.ico" will work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. --> diff --git a/items/frontend/public/manifest.json b/items/frontend/public/manifest.json index 69df9e1..1729cc0 100644 --- a/items/frontend/public/manifest.json +++ b/items/frontend/public/manifest.json @@ -3,12 +3,12 @@ "name": "Items Website for sp-tarkov", "icons": [ { - "src": "favicon.ico", + "src": "favico.ico", "sizes": "32x32", "type": "image/x-icon" }, { - "src": "favicon.svg", + "src": "favico.svg", "type": "image/svg+xml", "sizes": "64x64 96x96 128x128 160x160 192x192 512x512" } @@ -17,4 +17,4 @@ "display": "standalone", "theme_color": "#000000", "background_color": "#ffffff" -} +} \ No newline at end of file diff --git a/items/frontend/src/components/Footer.tsx b/items/frontend/src/components/Footer.tsx index 82a1f97..e2f7541 100644 --- a/items/frontend/src/components/Footer.tsx +++ b/items/frontend/src/components/Footer.tsx @@ -1,23 +1,23 @@ -import { Box, Theme, Typography } from '@mui/material' -import { makeStyles } from '@mui/styles' +import {Box, Typography} from '@mui/material' +import {makeStyles} from '@mui/styles' -const useStyles = makeStyles((theme: Theme) => ({ - footerHolder: { - display: 'flex', - flex: '0 1 3vh', - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - padding: '0 10vw 0 10vw' - } +const useStyles = makeStyles(() => ({ + footerHolder: { + display: 'flex', + flex: '0 1 3vh', + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + padding: '0 10vw 0 10vw' + } })) export const Footer = () => { - const classes = useStyles() + const classes = useStyles() - return ( - - SPT-Aki ©2021 Created by Rev and Shirito - - ) + return ( + + SPT-Aki ©2021 Created by Rev and Shirito + + ) } diff --git a/items/frontend/src/cypress/fixtures/condensed_milk.json b/items/frontend/src/cypress/fixtures/condensed_milk.json new file mode 100644 index 0000000..8493398 --- /dev/null +++ b/items/frontend/src/cypress/fixtures/condensed_milk.json @@ -0,0 +1,84 @@ +{ + "item": { + "_id": "5734773724597737fd047c14", + "_name": "condensed_milk", + "_parent": "5448e8d04bdc2ddf718b4569", + "_props": { + "AllowSpawnOnLocations": [], + "AnimationVariantsNumber": 0, + "BackgroundColor": "orange", + "CanRequireOnRagfair": true, + "CanSellOnRagfair": true, + "ChangePriceCoef": 1, + "ConflictingItems": [], + "CreditsPrice": 24943, + "Description": "Condensed milk", + "DiscardingBlock": false, + "ExamineExperience": 20, + "ExamineTime": 1, + "ExaminedByDefault": false, + "ExtraSizeDown": 0, + "ExtraSizeForceAdd": false, + "ExtraSizeLeft": 0, + "ExtraSizeRight": 0, + "ExtraSizeUp": 0, + "FixedPrice": false, + "Height": 1, + "HideEntrails": false, + "IsAlwaysAvailableForInsurance": false, + "IsLockedafterEquip": false, + "IsUnbuyable": false, + "IsUndiscardable": false, + "IsUngivable": false, + "IsUnsaleable": false, + "ItemSound": "food_tin_can", + "LootExperience": 50, + "MaxResource": 1, + "MergesWithChildren": false, + "Name": "Condensed milk", + "NotShownInSlot": false, + "Prefab": { + "path": "assets/content/weapons/usable_items/item_food_condensed_milk/item_food_condensed_milk_loot.bundle", + "rcid": "" + }, + "QuestItem": false, + "RagFairCommissionModifier": 1, + "Rarity": "Rare", + "RepairCost": 0, + "RepairSpeed": 0, + "SendToClient": false, + "ShortName": "Condensed milk", + "SpawnChance": 9, + "StackMaxSize": 1, + "StackObjectsCount": 1, + "StimulatorBuffs": "Buffs_food_condensed_milk", + "Unlootable": false, + "UnlootableFromSide": [], + "UnlootableFromSlot": "FirstPrimaryWeapon", + "UsePrefab": { + "path": "assets/content/weapons/usable_items/item_food_condensed_milk/item_food_condensed_milk_container.bundle", + "rcid": "" + }, + "Weight": 0.4, + "Width": 1, + "effects_damage": [], + "effects_health": { + "Energy": { + "value": 75 + }, + "Hydration": { + "value": -65 + } + }, + "foodEffectType": "afterUse", + "foodUseTime": 4 + }, + "_proto": "5734770f24597738025ee254", + "_type": "Item" + }, + "locale": { + "Description": "Condensed milk, also called \"Sguschyonka\" in Russia, once was a part of field ration for the Union soldiers in Civil War, but later reached unprecedented popularity in post-Soviet countries, becoming almost a staple product. Canned, it can be stored for decades and remain just as sweet, tasty and nutritious.", + "Name": "Condensed milk", + "ShortName": "Cond. milk" + } +} \ No newline at end of file diff --git a/items/frontend/src/cypress/integration/dark-mode-toggle.spec.tsx b/items/frontend/src/cypress/integration/dark-mode-toggle.spec.tsx index f010fbc..bcd8300 100644 --- a/items/frontend/src/cypress/integration/dark-mode-toggle.spec.tsx +++ b/items/frontend/src/cypress/integration/dark-mode-toggle.spec.tsx @@ -4,7 +4,7 @@ describe('Dark Mode Toggle', () => { beforeEach(() => { cy.window() .its("sessionStorage") - .invoke("removeItem", "items.sp-tarkov.com-locales"); + .invoke("removeItem", "db.sp-tarkov.com-locales"); cy.intercept( '**/api/locales', @@ -35,7 +35,7 @@ describe('Dark Mode Toggle', () => { before(() => { cy.visit('/', { onBeforeLoad: function (window) { - window.localStorage.removeItem('items.sp-tarkov.com-prefered-color-scheme') + window.localStorage.removeItem('db.sp-tarkov.com-prefered-color-scheme') } }) }) @@ -55,7 +55,7 @@ describe('Dark Mode Toggle', () => { before(() => { cy.visit('/', { onBeforeLoad: function (window) { - window.localStorage.setItem('items.sp-tarkov.com-prefered-color-scheme', 'light') + window.localStorage.setItem('db.sp-tarkov.com-prefered-color-scheme', 'light') } }) }) @@ -74,7 +74,7 @@ describe('Dark Mode Toggle', () => { before(() => { cy.visit('/', { onBeforeLoad: function (window) { - window.localStorage.setItem('items.sp-tarkov.com-prefered-color-scheme', 'dark') + window.localStorage.setItem('db.sp-tarkov.com-prefered-color-scheme', 'dark') } }) }) @@ -94,7 +94,7 @@ describe('Dark Mode Toggle', () => { before(() => { cy.visit('/', { onBeforeLoad: function (window) { - window.localStorage.setItem('items.sp-tarkov.com-prefered-color-scheme', 'light') + window.localStorage.setItem('db.sp-tarkov.com-prefered-color-scheme', 'light') } }) }) @@ -123,7 +123,7 @@ describe('Dark Mode Toggle', () => { before(() => { cy.visit('/', { onBeforeLoad: function (window) { - window.localStorage.setItem('items.sp-tarkov.com-prefered-color-scheme', 'dark') + window.localStorage.setItem('db.sp-tarkov.com-prefered-color-scheme', 'dark') } }) }) diff --git a/items/frontend/src/cypress/integration/footer.spec.tsx b/items/frontend/src/cypress/integration/footer.spec.tsx index ec80972..74ebb72 100644 --- a/items/frontend/src/cypress/integration/footer.spec.tsx +++ b/items/frontend/src/cypress/integration/footer.spec.tsx @@ -4,7 +4,7 @@ describe('Footer', () => { it('footer should be visible', () => { cy.visit('/'); - cy.get('.makeStyles-footerHolder-63 > .MuiTypography-root').should('be.visible') - cy.get('.makeStyles-footerHolder-63 > .MuiTypography-root').should('have.text', 'SPT-Aki ©2021 Created by Rev and Shirito'); + cy.get('#footer').should('be.visible') + cy.get('#footer').should('have.text', 'SPT-Aki ©2021 Created by Rev and Shirito'); }) }) diff --git a/items/frontend/src/cypress/integration/header.spec.tsx b/items/frontend/src/cypress/integration/header.spec.tsx index 55fe679..d28134f 100644 --- a/items/frontend/src/cypress/integration/header.spec.tsx +++ b/items/frontend/src/cypress/integration/header.spec.tsx @@ -4,7 +4,7 @@ describe('Header', () => { beforeEach(() => { cy.window() .its("sessionStorage") - .invoke("removeItem", "items.sp-tarkov.com-locales"); + .invoke("removeItem", "db.sp-tarkov.com-locales"); cy.intercept( '**/api/locales', diff --git a/items/frontend/src/cypress/integration/json-theme.spec.tsx b/items/frontend/src/cypress/integration/json-theme.spec.tsx index e8a6b4d..3512724 100644 --- a/items/frontend/src/cypress/integration/json-theme.spec.tsx +++ b/items/frontend/src/cypress/integration/json-theme.spec.tsx @@ -4,7 +4,7 @@ describe('Json Theme', () => { beforeEach(() => { cy.window() .its("sessionStorage") - .invoke("removeItem", "items.sp-tarkov.com-locales"); + .invoke("removeItem", "db.sp-tarkov.com-locales"); cy.intercept( '**/api/locales', @@ -35,7 +35,7 @@ describe('Json Theme', () => { before(() => { cy.visit('/', { onBeforeLoad: function (window) { - window.localStorage.removeItem('items.sp-tarkov.com-prefered-json-theme') + window.localStorage.removeItem('db.sp-tarkov.com-prefered-json-theme') } }); }) @@ -54,7 +54,7 @@ describe('Json Theme', () => { before(() => { cy.visit('/', { onBeforeLoad: function (window) { - window.localStorage.setItem('items.sp-tarkov.com-prefered-json-theme', 'eighties') + window.localStorage.setItem('db.sp-tarkov.com-prefered-json-theme', 'eighties') } }); }) diff --git a/items/frontend/src/cypress/integration/locale-select.spec.tsx b/items/frontend/src/cypress/integration/locale-select.spec.tsx index 1b892b8..f9301d4 100644 --- a/items/frontend/src/cypress/integration/locale-select.spec.tsx +++ b/items/frontend/src/cypress/integration/locale-select.spec.tsx @@ -4,7 +4,7 @@ describe('Locale select', () => { beforeEach(() => { cy.window() .its("sessionStorage") - .invoke("removeItem", "items.sp-tarkov.com-locales"); + .invoke("removeItem", "db.sp-tarkov.com-locales"); cy.intercept( '**/api/locales', @@ -31,7 +31,7 @@ describe('Locale select', () => { []).as('getLocalesWithoutData'); cy.visit('/', { onBeforeLoad: function (window) { - window.localStorage.removeItem('items.sp-tarkov.com-prefered-locale') + window.localStorage.removeItem('db.sp-tarkov.com-prefered-locale') } }) }) @@ -60,7 +60,7 @@ describe('Locale select', () => { before(() => { cy.visit('/', { onBeforeLoad: function (window) { - window.localStorage.removeItem('items.sp-tarkov.com-prefered-locale') + window.localStorage.removeItem('db.sp-tarkov.com-prefered-locale') } }) }) @@ -77,7 +77,7 @@ describe('Locale select', () => { before(() => { cy.visit('/', { onBeforeLoad: function (window) { - window.localStorage.setItem('items.sp-tarkov.com-prefered-locale', 'locale2') + window.localStorage.setItem('db.sp-tarkov.com-prefered-locale', 'locale2') } }) }) diff --git a/items/frontend/src/cypress/integration/search.spec.tsx b/items/frontend/src/cypress/integration/search.spec.tsx index 9c7c60b..62eceea 100644 --- a/items/frontend/src/cypress/integration/search.spec.tsx +++ b/items/frontend/src/cypress/integration/search.spec.tsx @@ -1,11 +1,40 @@ +import condensedMilk from "../fixtures/condensed_milk.json"; + export {}; describe('Search area', ()=>{ beforeEach(() => { - cy.intercept('GET','**/api/locales', { + cy.intercept({ + method: 'GET', + url: '**/api/locales' + }, { statusCode: 200, body: ['locale1', 'locale2'] - }); + }).as('getLocalesWithoutData'); + + cy.intercept({ + method: 'GET', + url: '**/api/item?*' + }, { + statusCode: 200, + body: condensedMilk + }) + + cy.intercept({ + method: 'POST', + url: '**/api/search*' + }, { + statusCode: 200, + body: [] + }) + + cy.intercept({ + method: 'GET', + url: '**/api/item/hierarchy*' + }, { + statusCode: 200, + body: condensedMilk + }) cy.visit('/'); }) @@ -31,4 +60,19 @@ describe('Search area', ()=>{ .should('be.visible') .contains('p', 'No data to display'); }) + + describe('Searching', () => { + it('Search using ID displays the json with locale', () => { + cy.get('#search-autocomplete') + .type(condensedMilk.item._id) + .type('{ENTER}') + .should('have.value', condensedMilk.locale.Name) + cy.get(`.react-json-view .object-key-val > .pushed-content > .object-content > .variable-row > .variable-value > div > .string-value:contains("${condensedMilk.item._id}")`) + .should('have.length', 1) + .invoke('text') + .should('eq', `"${condensedMilk.item._id}"`); + cy.get(`.react-json-view .object-key-val > .pushed-content > .object-content > .variable-row > .variable-value > div > .string-value:contains("${condensedMilk.locale.Name}")`) + .should('have.length.above', 0); + }) + }) }) \ No newline at end of file diff --git a/items/frontend/src/cypress/integration/url-check.spec.tsx b/items/frontend/src/cypress/integration/url-check.spec.tsx new file mode 100644 index 0000000..17dc03e --- /dev/null +++ b/items/frontend/src/cypress/integration/url-check.spec.tsx @@ -0,0 +1,82 @@ +import condensedMilk from '../fixtures/condensed_milk.json' + +export {}; + +describe('Url check', () => { + beforeEach(() => { + cy.window() + .its("sessionStorage") + .invoke("removeItem", "db.sp-tarkov.com-locales"); + + cy.intercept({ + method: 'GET', + url: '**/api/locales' + }, { + statusCode: 200, + body: [] + }).as('getLocalesWithoutData'); + + cy.intercept({ + method: 'GET', + url: '**/api/item?*' + }, { + statusCode: 200, + body: condensedMilk + }) + + cy.intercept({ + method: 'POST', + url: '**/api/search*' + }, { + statusCode: 200, + body: [] + }) + + + cy.intercept({ + method: 'GET', + url: '**/api/item/hierarchy*' + }, { + statusCode: 200, + body: condensedMilk + }) + }) + + afterEach(() => { + cy.clearLocalStorage(); + }) + + describe('Check page not found', () => { + it('Invalid url should redirect to page not found', () => { + cy.visit('/ABC') + cy.get('#not-found-message').contains("This page does not exist !"); + }) + }) + + describe('Check root redirection', () => { + it('Root should redirect to search', () => { + cy.visit('/') + cy.url().should("include", "/search"); + }) + }) + + describe('Check url changes with search input', () => { + it('ID in url applies search', () => { + cy.visit(`/search/${condensedMilk.item._id}`); + cy.get('#search-autocomplete').should('have.value', condensedMilk.locale.Name) + }) + + it('Search reflects in url', () => { + cy.visit(`/`); + cy.get('#search-autocomplete') + .type(condensedMilk.item._id) + .type('{ENTER}') + .should('have.value', condensedMilk.locale.Name); + cy.url().should("include", `/search/${condensedMilk.item._id}`); + cy.get(`.react-json-view .object-key-val > .pushed-content > .object-content > .variable-row > .variable-value > div > .string-value:contains("${condensedMilk.item._id}")`) + .should('have.length', 1) + .invoke('text') + .should('eq', `"${condensedMilk.item._id}"`); + }) + }) +}) \ No newline at end of file diff --git a/items/frontend/src/cypress/nginx_config/default.conf b/items/frontend/src/cypress/nginx_config/default.conf new file mode 100644 index 0000000..40b9650 --- /dev/null +++ b/items/frontend/src/cypress/nginx_config/default.conf @@ -0,0 +1,15 @@ +server { + listen 80; + server_name localhost; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri $uri/ /index.html; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} diff --git a/items/frontend/src/dataaccess/ItemBackend.ts b/items/frontend/src/dataaccess/ItemBackend.ts index 526bc9a..fb66724 100644 --- a/items/frontend/src/dataaccess/ItemBackend.ts +++ b/items/frontend/src/dataaccess/ItemBackend.ts @@ -4,12 +4,16 @@ import { ItemData } from '../dto/ItemData'; import { useGlobalState } from '../state/GlobalState'; const handleFetch = async (url: string, init?: RequestInit | undefined): Promise => { - const resp = await fetch(url, { ...init, mode: 'cors' }) - if (resp.status === 200) { - const jsonResponse = await resp.json(); - return jsonResponse !== undefined ? jsonResponse : null; - } else if (resp.status >= 400) { - console.error(resp) + try { + const resp = await fetch(url, { ...init, mode: 'cors' }) + if (resp.status === 200) { + const jsonResponse = await resp.json(); + return jsonResponse !== undefined ? jsonResponse : null; + } else if (resp.status >= 400) { + console.warn(resp) + } + }catch (e) { + console.warn(e) } return null; } diff --git a/items/frontend/src/dataaccess/SaveKeys.ts b/items/frontend/src/dataaccess/SaveKeys.ts index f3c7b58..2063df2 100644 --- a/items/frontend/src/dataaccess/SaveKeys.ts +++ b/items/frontend/src/dataaccess/SaveKeys.ts @@ -1,10 +1,10 @@ export enum LocalStorageKeys { - PREFERED_COLOR_SCHEME = 'items.sp-tarkov.com-prefered-color-scheme', - PREFERED_JSON_THEME = 'items.sp-tarkov.com-prefered-json-theme', - PREFERED_LOCALE = 'items.sp-tarkov.com-prefered-locale' + PREFERED_COLOR_SCHEME = 'db.sp-tarkov.com-prefered-color-scheme', + PREFERED_JSON_THEME = 'db.sp-tarkov.com-prefered-json-theme', + PREFERED_LOCALE = 'db.sp-tarkov.com-prefered-locale' } export enum SessionStorageKeys { - LOCALES = 'items.sp-tarkov.com-locales', - ITEMS_HIERARCHY = 'items.sp-tarkov.com-items-hierarchy', + LOCALES = 'db.sp-tarkov.com-locales', + ITEMS_HIERARCHY = 'db.sp-tarkov.com-items-hierarchy', } \ No newline at end of file diff --git a/items/frontend/src/index.tsx b/items/frontend/src/index.tsx index 7998135..6031b20 100644 --- a/items/frontend/src/index.tsx +++ b/items/frontend/src/index.tsx @@ -4,13 +4,13 @@ import App from './App'; import reportWebVitals from './reportWebVitals'; ReactDOM.render( - - - , - document.getElementById('root') + + + , + document.getElementById('root') ); // If you want to start measuring performance in your app, pass a function // to log results (for example: reportWebVitals(console.log)) // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(); +reportWebVitals(); \ No newline at end of file diff --git a/items/frontend/src/pages/InteractiveArea.tsx b/items/frontend/src/pages/InteractiveArea.tsx new file mode 100644 index 0000000..34a79d7 --- /dev/null +++ b/items/frontend/src/pages/InteractiveArea.tsx @@ -0,0 +1,26 @@ +import {Box} from '@mui/material' +import {NavigationBreadcrumb} from './mainPageComponents/NavigationBreadcrumb' +import {SearchArea} from './mainPageComponents/SearchArea' +import {makeStyles} from "@mui/styles"; +import React from "react"; + +const useStyles = makeStyles(() => ({ + searchContainer: { + display: 'flex', + flexDirection: 'row', + flexGrow: 1, + padding: '2vh 2vw 1vh 2vw' + } +})) + +export const InteractiveArea = () => { + const classes = useStyles(); + return ( + <> + + + + + + ) +} diff --git a/items/frontend/src/pages/MainPage.tsx b/items/frontend/src/pages/MainPage.tsx index a5c3b33..5b271e3 100644 --- a/items/frontend/src/pages/MainPage.tsx +++ b/items/frontend/src/pages/MainPage.tsx @@ -1,11 +1,13 @@ -import {Box, Theme} from '@mui/material' +import {Box} from '@mui/material' import {Footer} from '../components/Footer' import {Header} from '../components/Header' -import {NavigationBreadcrumb} from './mainPageComponents/NavigationBreadcrumb' -import {SearchArea} from './mainPageComponents/SearchArea' import {makeStyles} from "@mui/styles"; +import {BrowserRouter, Navigate, Route, Routes} from "react-router-dom"; +import React from "react"; +import {InteractiveArea} from "./InteractiveArea"; +import {PageNotFound} from "./PageNotFound"; -const useStyles = makeStyles((theme: Theme) => ({ +const useStyles = makeStyles(() => ({ container: { background: 'background.default', display: 'flex', @@ -13,12 +15,6 @@ const useStyles = makeStyles((theme: Theme) => ({ flexGrow: 1, height: '100vh', maxheight: '100vh', - }, - searchContainer: { - display: 'flex', - flexDirection: 'row', - flexGrow: 1, - padding: '2vh 2vw 1vh 2vw' } })) @@ -28,10 +24,15 @@ export const MainPage = () => { <>
- - - - + + + }/> + }/> + }/> + }/> + } /> + +