diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 033993a9a..fb7128103 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -81,7 +81,7 @@ jobs: - name: Get Changed Unauthorized files id: changed-unauth-files - uses: tj-actions/changed-files@v44 + uses: tj-actions/changed-files@v45 with: files: | .github/** diff --git a/.github/workflows/stale-issues-prs.yml b/.github/workflows/stale-issues-prs.yml index 9d34ce23d..64a6b1708 100644 --- a/.github/workflows/stale-issues-prs.yml +++ b/.github/workflows/stale-issues-prs.yml @@ -55,7 +55,7 @@ jobs: stale-pr-label: 'Status: Stale' # Number of days of inactivity before an issue/PR is marked as stale. - days-before-stale: 30 + days-before-stale: 180 # Number of days of inactivity before an issue/PR is closed. days-before-close: 180 diff --git a/.github/workflows/sync-contributors.yml b/.github/workflows/sync-contributors.yml index 7325c2910..7de8bb1b3 100644 --- a/.github/workflows/sync-contributors.yml +++ b/.github/workflows/sync-contributors.yml @@ -46,14 +46,42 @@ jobs: fs.writeFileSync('community.json', JSON.stringify(data, null, 2)); - name: Commit changes - env: - GITHUB_APP_TOKEN: ${{ steps.get_workflow_token.outputs.token }} run: | git config user.name "the-json-schema-bot[bot]" git config user.email "the-json-schema-bot[bot]@users.noreply.github.com" git add community.json - git diff --quiet && git diff --staged --quiet || (git commit -m "chore(community): update community.json" && git push "https://x-access-token:${GITHUB_APP_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" HEAD:${GITHUB_REF#refs/heads/}) - + git diff --quiet && git diff --staged --quiet || (git commit -m "chore(community): update community.json") + + - name: Create Pull Request + id: cpr + uses: peter-evans/create-pull-request@v6 + with: + token: ${{ steps.get_workflow_token.outputs.token }} + title: 'chore(community): update community.json' + body: 'This PR updates the community.json file with the latest contributors data.' + branch: 'update-contributors' + base: 'main' + author: 'the-json-schema-bot ' + branch-suffix: timestamp + labels: 'sync-contributors' + assignees: 'the-json-schema-bot' + draft: false + signoff: true + add-paths: 'community.json' + + # Approving with github bot token because one cannot approve their own PRs + - name: Auto Approve + if: steps.cpr.outputs.pull-request-operation == 'created' + run: gh pr review --approve "${{ steps.cpr.outputs.pull-request-number }}" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Auto Merge PR + if: steps.cpr.outputs.pull-request-operation == 'created' + uses: peter-evans/enable-pull-request-automerge@v3 + with: + pull-request-number: ${{ steps.cpr.outputs.pull-request-number }} + merge-method: 'squash' + token: ${{ steps.get_workflow_token.outputs.token }} diff --git a/.github/workflows/sync-project-roadmap.yml b/.github/workflows/sync-project-roadmap.yml index 238af4819..6e63a7fe0 100644 --- a/.github/workflows/sync-project-roadmap.yml +++ b/.github/workflows/sync-project-roadmap.yml @@ -90,11 +90,41 @@ jobs: # commit updated project data - name: Commit changes - env: - GITHUB_APP_TOKEN: ${{ steps.get_workflow_token.outputs.token }} run: | git config user.name "the-json-schema-bot[bot]" git config user.email "the-json-schema-bot[bot]@users.noreply.github.com" git add project_data.json - git diff --quiet && git diff --staged --quiet || (git commit -m "chore(project_data): update project_data.json" && git push "https://x-access-token:${GITHUB_APP_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" HEAD:${GITHUB_REF#refs/heads/}) + git diff --quiet && git diff --staged --quiet || (git commit -m "chore(project_data): update project_data.json") + + # create a pull request + - name: Create Pull Request + id: cpr + uses: peter-evans/create-pull-request@v6 + with: + token: ${{ steps.get_workflow_token.outputs.token }} + title: 'chore(project_data): update project_data.json' + body: 'This PR updates the project_data.json file with the latest project data.' + branch: 'update-project-data' + base: 'main' + author: 'the-json-schema-bot ' + branch-suffix: timestamp + labels: 'sync-project-data' + assignees: 'the-json-schema-bot' + draft: false + signoff: true + add-paths: 'project_data.json' + # Approving with github bot token because one cannot approve their own PRs + - name: Auto Approve + if: steps.cpr.outputs.pull-request-operation == 'created' + run: gh pr review --approve "${{ steps.cpr.outputs.pull-request-number }}" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Auto Merge PR + if: steps.cpr.outputs.pull-request-operation == 'created' + uses: peter-evans/enable-pull-request-automerge@v3 + with: + pull-request-number: ${{ steps.cpr.outputs.pull-request-number }} + merge-method: 'squash' + token: ${{ steps.get_workflow_token.outputs.token }} \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 75a3a9d1e..4dede74b3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,9 +21,12 @@ If you don't have time to contribute, that's fine. There are other easy ways to - ๐Ÿ“‹ [Improving the Documentation](#-improving-the-documentation) - ๐ŸŒ [Improving the website](#-improving-the-website) - ๐ŸŽจ [Improving the Design](#-improving-the-design) + - ๐Ÿš€ [Contributing to CI/CD Pipeline](#-contributing-to-cicd-pipeline) + - ๐Ÿงช [Contributing to Testing](#-contributing-to-testing) - โŒจ๏ธ [Pull requests](#%EF%B8%8F-pull-requests) - ๐Ÿ› [License](#-license) + ## ๐ŸŒฑ Code of Conduct Before making your first contribution, please ensure you are familiar with our [Code of Conduct](https://github.com/json-schema-org/.github/blob/main/CODE_OF_CONDUCT.md). @@ -60,7 +63,7 @@ To publish a case study, we encourage you to join `#adopters` channel in our [Sl If you would like to join the efforts to improve the JSON Schema Documentation, we encourage you to check our [documentation board](https://github.com/orgs/json-schema-org/projects/16) to get a sense of the pending issues and who is doing what. We encourage you to join the discussion on the [issue #421](https://github.com/json-schema-org/community/issues/421) so you can see the long term vision for our docs. We also invite you to join the discussion in `#documentation` channel in our [Slack workspace](https://json-schema.org/slack). -### ๐Ÿ“‹ Improving the Website +### ๐ŸŒ Improving the Website If you would like to join the efforts to improve the JSON Schema Website, we encourage you to check our [website contribution board](https://github.com/orgs/json-schema-org/projects/11) to get a sense of the pending issues and bugs and who is doing what. We also invite you to join the discussion in `#website` channel in our [Slack workspace](https://json-schema.org/slack). @@ -68,6 +71,33 @@ If you would like to join the efforts to improve the JSON Schema Website, we enc If you would like to contribute with designs, we encourage you to join `#design` channel in our [Slack workspace](https://json-schema.org/slack) and read the [contributing guidelines](https://github.com/json-schema-org/brand/blob/master/CONTRIBUTING.md) in the [Brand](https://github.com/json-schema-org/brand) repository. +### ๐Ÿš€ Contributing to CI/CD Pipeline + +If you would like to contribute to our CI/CD pipeline, we encourage you to review our current setup in the [workflows README](https://github.com/json-schema-org/website/blob/main/.github/workflows/README.md). + +### ๐Ÿงช Contributing to Testing + +We value contributions to our testing efforts. Here are ways you can help improve our test coverage and quality: + +1. **Writing Tests**: If you're adding new features or fixing bugs, please include relevant tests. We use Cypress for both end-to-end (E2E) and component testing. + + - For new components, add component tests in the `cypress/components` directory. + - For new features or bug fixes affecting user interactions, add E2E tests in the `cypress/e2e` directory. + +2. **Improving Existing Tests**: Review and enhance our existing test suite. Look for areas where test coverage could be improved or where tests could be made more robust. + +3. **Test Documentation**: Help improve our testing documentation, making it easier for new contributors to understand and write tests. + +4. **Running Tests**: Before submitting a pull request, ensure all tests pass by running: + ``` + yarn cypress:run:all + ``` + +5. **Reporting Test Issues**: If you find inconsistencies or problems with our tests, please open an issue describing the problem and how to reproduce it. + +For more details on our testing setup and how to run tests, please refer to the Testing section in our [INSTALLATION.md](./INSTALLATION.md#testing) file. + + ### โŒจ๏ธ Pull requests We welcome pull requests for editorial suggestions and resolving open issues. diff --git a/INSTALLATION.md b/INSTALLATION.md new file mode 100644 index 000000000..3309711a3 --- /dev/null +++ b/INSTALLATION.md @@ -0,0 +1,286 @@ +# JSON Schema Website: Installation and Development Guide + +This guide provides step-by-step instructions for installing the JSON Schema Website on your local machine. Follow each section in order without skipping any steps. + +## Table of Contents + +1. [Setting Up the Project](#setting-up-the-project) + - [Requirements](#requirements) + - [Cloning the Repository](#cloning-the-repository) + - [Setting Up Environment Variables](#setting-up-environment-variables) +2. [Corepack Configuration](#corepack-configuration) + - [What is Corepack?](#what-is-corepack) + - [Installing Corepack](#installing-corepack) + - [Using Corepack with This Project](#using-corepack-with-this-project) + - [Updating Yarn Version](#updating-yarn-version) + - [Troubleshooting](#troubleshooting) +3. [Post-Configuration Steps](#post-configuration-steps) + - [Installing Dependencies](#installing-dependencies) + - [Running the Development Server](#running-the-development-server) + - [Building Static Files](#building-static-files) +4. [Testing](#testing) + - [Running Tests](#running-tests) + - [Test Coverage](#test-coverage) + - [Writing Tests](#writing-tests) +5. [Code Quality](#code-quality) + - [Formatting](#formatting) + - [Linting](#linting) + - [Husky for Git Hooks](#husky-for-git-hooks) +6. [Run locally using Docker](#docker-deployment) + - [Prerequisites](#prerequisites) + - [Steps](#steps) + + +## Setting up Project + +## Configuration + +### Requirements +Use the following tools to set up the project: + +Node.js v20.9.0+ + +### Cloning the repository +This project uses git submodules, so you will need to run the following commands to fully clone the repo. +``` +git submodule init +git submodule update +``` + +### Setting Up Environment Variables + +1. Create a new `.env` file by copying the contents of the `.env.example` into `.env` file. Use this command: +``` +cp .env.example .env +``` +2. Open .env and fill in your actual values for each variable. + +3. Save the file. + +4. Ensure .env is in your .gitignore. + +### Corepack Configuration + +This project uses modern Yarn (yarn@4.4.0), which requires Corepack for proper setup and management of package managers. Corepack is a tool that comes with Node.js 14.19.0 and later, allowing for consistent package manager versions across your project. + +#### What is Corepack? + +Corepack is an experimental tool to help with managing versions of your package managers. It exposes binary proxies for each supported package manager that, when called, will identify whatever package manager is configured for the current project, download it if needed, and finally run it. + + +#### Installing Corepack + +If you're using Node.js version 14.19.0 or later, Corepack is included but might need to be enabled. For Node.js 16.10 or later, Corepack is available by default but might still need to be enabled. + +To enable Corepack, run: + +```bash +corepack enable +``` + +If you're using an older version of Node.js or if the above command doesn't work, you can install Corepack globally using npm: + +```bash +npm install -g corepack +``` + +#### Using Corepack with This Project + +Once Corepack is enabled or installed, it will automatically use the correct version of Yarn specified in the project's `package.json` file. You don't need to manually install Yarn. + +To use Yarn commands, simply run them as usual: + +```bash +yarn install +yarn run build +yarn run dev +``` + +Corepack will ensure that the correct version of Yarn is used for these commands. + +#### Updating Yarn Version + +If you need to update the Yarn version used in the project: + +1. Update the `packageManager` field in `package.json`: + ```json + { + "packageManager": "yarn@x.y.z" + } + ``` +2. Run `yarn set version x.y.z` to update the local Yarn version. + +#### Troubleshooting + +If you encounter any issues with Yarn commands, try running: + +```bash +corepack prepare +``` + +This will ensure that the correct version of Yarn is downloaded and prepared for use. + +For more information about Corepack, refer to the [official Node.js documentation](https://nodejs.org/api/corepack.html). + +## Post-Configuration Steps + +### Installing Dependencies + +Install dependencies +``` +yarn +``` + +### Running the Development Server + +Run the development server on http://localhost:3000 +``` +yarn dev +``` + +### Building Static Files + +Build static files on `/out` folder +``` +yarn build +``` +## Testing + +We use Cypress for both end-to-end (E2E) testing and component testing. This document will guide you through the process of running tests and generating coverage reports. + +### Running Tests + + +#### Opening Cypress Test Runner + +To open the Cypress Test Runner, which allows you to run tests interactively, use: + +``` +yarn cypress:open +``` + +This command will open a GUI where you can select and run specific tests. + +#### Running All Tests Headlessly + +To run all tests in headless mode (useful for CI/CD pipelines), use: + +``` +yarn cypress:run:all +``` + +### Test Coverage + +We use NYC (Istanbul) for code coverage reporting. Here's how to generate coverage reports: + +#### E2E Test Coverage + +To run E2E tests with coverage: + +``` +yarn test:coverage:e2e +``` + +#### Component Test Coverage + +To run component tests with coverage: + +``` +yarn test:coverage:component +``` + +#### Full Test Coverage + +To run all tests and generate a combined coverage report: + +``` +yarn test:coverage:all +``` + +This command will: +1. Run E2E tests with coverage +2. Run component tests with coverage +3. Merge the coverage results +4. Generate an HTML and text report + +You can find the HTML report in the `coverage` directory after running this command. + +### Writing Tests + +When contributing new features or fixing bugs, please consider adding or updating tests: + +- For new components, add component tests in the relevant `cypress/components` directory. +- For new features or bug fixes affecting user interactions, add E2E tests in the `cypress/e2e` directory. + +## Code Quality + +We use ESLint for linting and Prettier for code formatting. This section will guide you through the process of checking and fixing code quality issues. + +### Formatting + +you can check code formatting using the following command: + +``` +yarn run format:check +``` + +you can format the code using the following command: + +``` +yarn run format:fix +``` + +### Linting + +you can check linting issues using the following command: + +``` +yarn run lint +``` + +you can fix linting issues using the following command: + +``` +yarn run lint:fix +``` + +### Husky for git hooks + +This project uses Husky to run checks for the formatting, linting, typecheck and build commands before committing the code. + +#### pre-commit hook +pre-commit hook will run the following commands: + +``` +yarn run lint +yarn run typecheck +yarn run build +``` + +If you don't want these pre-commit checks running on each commit, you can manually opt out of it using the `--no-verify` flag with your commit message as shown:- +``` +git commit -m "commit message" --no-verify +``` + +### Run locally using Docker + +If you are a Docker lover, you have the option to use it following these instructions. + +#### Prerequisites: + +- [Install Docker](https://docs.docker.com/get-docker/) + +After cloning repository to your local, perform the following steps from the root of the repository. + +#### Steps: +1. Build the Docker image: + ```bash + make install + ``` + +2. Start the container: + ```bash + make run + ``` + +Now you're running JSON Schema website in a development mode. Container is mapped with your local copy of the website. Whenever you make changes to the code, the website will refresh and changes visible in `http://localhost:3000`. diff --git a/README.md b/README.md index 427469329..70f9e6cff 100644 --- a/README.md +++ b/README.md @@ -26,179 +26,9 @@ This repository contains the sources of JSON Schema website: * It uses Tailwind CSS framework, * It's build and deployed with Netlify. -## Run locally +## Setting up Project -#### Requirements -Use the following tools to set up the project: - -Node.js v20.9.0+ - -#### Cloning the repository -This project uses git submodules, so you will need to run the following commands to fully clone the repo. -``` -git submodule init -git submodule update -``` - -### Setup Environment Variables - -1. Create a new `.env` file by copying the contents of the `.env.example` into `.env` file. Use this command: -``` -cp .env.example .env -``` -2. Open .env and fill in your actual values for each variable. - -3. Save the file. - -4. Ensure .env is in your .gitignore. - -### Setup Corepack - -This project uses modern Yarn (yarn@4.4.0), which requires Corepack for proper setup and management of package managers. Corepack is a tool that comes with Node.js 14.19.0 and later, allowing for consistent package manager versions across your project. - -#### What is Corepack? - -Corepack is an experimental tool to help with managing versions of your package managers. It exposes binary proxies for each supported package manager that, when called, will identify whatever package manager is configured for the current project, download it if needed, and finally run it. - - -#### Installing Corepack - -If you're using Node.js version 14.19.0 or later, Corepack is included but might need to be enabled. For Node.js 16.10 or later, Corepack is available by default but might still need to be enabled. - -To enable Corepack, run: - -```bash -corepack enable -``` - -If you're using an older version of Node.js or if the above command doesn't work, you can install Corepack globally using npm: - -```bash -npm install -g corepack -``` - -#### Using Corepack with This Project - -Once Corepack is enabled or installed, it will automatically use the correct version of Yarn specified in the project's `package.json` file. You don't need to manually install Yarn. - -To use Yarn commands, simply run them as usual: - -```bash -yarn install -yarn run build -yarn run dev -``` - -Corepack will ensure that the correct version of Yarn is used for these commands. - -#### Updating Yarn Version - -If you need to update the Yarn version used in the project: - -1. Update the `packageManager` field in `package.json`: - ```json - { - "packageManager": "yarn@x.y.z" - } - ``` -2. Run `yarn set version x.y.z` to update the local Yarn version. - -#### Troubleshooting - -If you encounter any issues with Yarn commands, try running: - -```bash -corepack prepare -``` - -This will ensure that the correct version of Yarn is downloaded and prepared for use. - -For more information about Corepack, refer to the [official Node.js documentation](https://nodejs.org/api/corepack.html). - -#### Install dependencies - -Install dependencies -``` -yarn -``` - -#### Run the development server - -Run the development server on http://localhost:3000 -``` -yarn dev -``` - -#### Build static files - -Build static files on /out folder -``` -yarn build -``` -#### Testing - -Formatting - -you can check code formatting using the following command: - -``` -yarn run format:check -``` - -you can format the code using the following command: - -``` -yarn run format:fix -``` - -Linting - -you can check linting issues using the following command: - -``` -yarn run lint -``` - -you can fix linting issues using the following command: - -``` -yarn run lint:fix -``` - -Husky for git hooks - -This project uses Husky to run checks for the formatting, linting, typecheck and build commands before committing the code. - -pre-commit hook will run the following commands: - -``` -yarn run lint -yarn run typecheck -yarn run build -``` - -### Run locally using Docker - -If you are a Docker lover, you have the option to use it following these instructions. - -#### Prerequisites: - -- [install Docker](https://docs.docker.com/get-docker/) - -After cloning repository to your local, perform the following steps from the root of the repository. - -#### Steps: -1. Build the Docker image: - ```bash - make install - ``` - -2. Start the container: - ```bash - make run - ``` - -Now you're running JSON Schema website in a development mode. Container is mapped with your local copy of the website. Whenever you make changes to the code, the website will refresh and changes visible in localhost:3000. +Please refer to the [INSTALLATION.md](./INSTALLATION.md) file for instructions on how to set up the project on your local machine. ## Project structure diff --git a/community.json b/community.json new file mode 100644 index 000000000..6dafdebf6 --- /dev/null +++ b/community.json @@ -0,0 +1,518 @@ +[ + { + "login": "benjagm", + "id": 40007659, + "avatar_url": "https://avatars.githubusercontent.com/u/40007659?v=4", + "html_url": "https://github.com/benjagm" + }, + { + "login": "musemind", + "id": 11320794, + "avatar_url": "https://avatars.githubusercontent.com/u/11320794?v=4", + "html_url": "https://github.com/musemind" + }, + { + "login": "aku1310", + "id": 72210364, + "avatar_url": "https://avatars.githubusercontent.com/u/72210364?v=4", + "html_url": "https://github.com/aku1310" + }, + { + "login": "dependabot[bot]", + "id": 49699333, + "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", + "html_url": "https://github.com/apps/dependabot" + }, + { + "login": "OlliesWorld", + "id": 51091185, + "avatar_url": "https://avatars.githubusercontent.com/u/51091185?v=4", + "html_url": "https://github.com/OlliesWorld" + }, + { + "login": "Relequestual", + "id": 731158, + "avatar_url": "https://avatars.githubusercontent.com/u/731158?v=4", + "html_url": "https://github.com/Relequestual" + }, + { + "login": "jviotti", + "id": 2192773, + "avatar_url": "https://avatars.githubusercontent.com/u/2192773?v=4", + "html_url": "https://github.com/jviotti" + }, + { + "login": "DhairyaMajmudar", + "id": 124715224, + "avatar_url": "https://avatars.githubusercontent.com/u/124715224?v=4", + "html_url": "https://github.com/DhairyaMajmudar" + }, + { + "login": "gregsdennis", + "id": 2676804, + "avatar_url": "https://avatars.githubusercontent.com/u/2676804?v=4", + "html_url": "https://github.com/gregsdennis" + }, + { + "login": "Julian", + "id": 329822, + "avatar_url": "https://avatars.githubusercontent.com/u/329822?v=4", + "html_url": "https://github.com/Julian" + }, + { + "login": "jdesrosiers", + "id": 716571, + "avatar_url": "https://avatars.githubusercontent.com/u/716571?v=4", + "html_url": "https://github.com/jdesrosiers" + }, + { + "login": "DarhkVoyd", + "id": 77478658, + "avatar_url": "https://avatars.githubusercontent.com/u/77478658?v=4", + "html_url": "https://github.com/DarhkVoyd" + }, + { + "login": "aialok", + "id": 66772290, + "avatar_url": "https://avatars.githubusercontent.com/u/66772290?v=4", + "html_url": "https://github.com/aialok" + }, + { + "login": "JeelRajodiya", + "id": 63534268, + "avatar_url": "https://avatars.githubusercontent.com/u/63534268?v=4", + "html_url": "https://github.com/JeelRajodiya" + }, + { + "login": "PaulWaller", + "id": 2253876, + "avatar_url": "https://avatars.githubusercontent.com/u/2253876?v=4", + "html_url": "https://github.com/PaulWaller" + }, + { + "login": "TamannaVerma99", + "id": 96949249, + "avatar_url": "https://avatars.githubusercontent.com/u/96949249?v=4", + "html_url": "https://github.com/TamannaVerma99" + }, + { + "login": "starlightknown", + "id": 74637789, + "avatar_url": "https://avatars.githubusercontent.com/u/74637789?v=4", + "html_url": "https://github.com/starlightknown" + }, + { + "login": "VivekJaiswal18", + "id": 96608169, + "avatar_url": "https://avatars.githubusercontent.com/u/96608169?v=4", + "html_url": "https://github.com/VivekJaiswal18" + }, + { + "login": "spacether", + "id": 1912028, + "avatar_url": "https://avatars.githubusercontent.com/u/1912028?v=4", + "html_url": "https://github.com/spacether" + }, + { + "login": "Sahil-Shadwal", + "id": 119167601, + "avatar_url": "https://avatars.githubusercontent.com/u/119167601?v=4", + "html_url": "https://github.com/Sahil-Shadwal" + }, + { + "login": "officeneerajsaini", + "id": 118799941, + "avatar_url": "https://avatars.githubusercontent.com/u/118799941?v=4", + "html_url": "https://github.com/officeneerajsaini" + }, + { + "login": "divyaxdv", + "id": 95571773, + "avatar_url": "https://avatars.githubusercontent.com/u/95571773?v=4", + "html_url": "https://github.com/divyaxdv" + }, + { + "login": "Dule-martins", + "id": 33032530, + "avatar_url": "https://avatars.githubusercontent.com/u/33032530?v=4", + "html_url": "https://github.com/Dule-martins" + }, + { + "login": "Gmin2", + "id": 127925465, + "avatar_url": "https://avatars.githubusercontent.com/u/127925465?v=4", + "html_url": "https://github.com/Gmin2" + }, + { + "login": "Adity20", + "id": 119804372, + "avatar_url": "https://avatars.githubusercontent.com/u/119804372?v=4", + "html_url": "https://github.com/Adity20" + }, + { + "login": "lateapexearlyspeed", + "id": 72254037, + "avatar_url": "https://avatars.githubusercontent.com/u/72254037?v=4", + "html_url": "https://github.com/lateapexearlyspeed" + }, + { + "login": "JulianCataldo", + "id": 603498, + "avatar_url": "https://avatars.githubusercontent.com/u/603498?v=4", + "html_url": "https://github.com/JulianCataldo" + }, + { + "login": "ayushtiwari110", + "id": 84335414, + "avatar_url": "https://avatars.githubusercontent.com/u/84335414?v=4", + "html_url": "https://github.com/ayushtiwari110" + }, + { + "login": "the-json-schema-bot[bot]", + "id": 171232837, + "avatar_url": "https://avatars.githubusercontent.com/in/909251?v=4", + "html_url": "https://github.com/apps/the-json-schema-bot" + }, + { + "login": "Sanket-0510", + "id": 104385297, + "avatar_url": "https://avatars.githubusercontent.com/u/104385297?v=4", + "html_url": "https://github.com/Sanket-0510" + }, + { + "login": "harrel56", + "id": 26698228, + "avatar_url": "https://avatars.githubusercontent.com/u/26698228?v=4", + "html_url": "https://github.com/harrel56" + }, + { + "login": "maverox", + "id": 29498864, + "avatar_url": "https://avatars.githubusercontent.com/u/29498864?v=4", + "html_url": "https://github.com/maverox" + }, + { + "login": "swaparup36", + "id": 82692857, + "avatar_url": "https://avatars.githubusercontent.com/u/82692857?v=4", + "html_url": "https://github.com/swaparup36" + }, + { + "login": "srini047", + "id": 81156510, + "avatar_url": "https://avatars.githubusercontent.com/u/81156510?v=4", + "html_url": "https://github.com/srini047" + }, + { + "login": "Codesmith28", + "id": 117557273, + "avatar_url": "https://avatars.githubusercontent.com/u/117557273?v=4", + "html_url": "https://github.com/Codesmith28" + }, + { + "login": "sandrina-p", + "id": 14869087, + "avatar_url": "https://avatars.githubusercontent.com/u/14869087?v=4", + "html_url": "https://github.com/sandrina-p" + }, + { + "login": "vinfinity7", + "id": 122719607, + "avatar_url": "https://avatars.githubusercontent.com/u/122719607?v=4", + "html_url": "https://github.com/vinfinity7" + }, + { + "login": "satyam-x10", + "id": 106728045, + "avatar_url": "https://avatars.githubusercontent.com/u/106728045?v=4", + "html_url": "https://github.com/satyam-x10" + }, + { + "login": "micshar92", + "id": 112581105, + "avatar_url": "https://avatars.githubusercontent.com/u/112581105?v=4", + "html_url": "https://github.com/micshar92" + }, + { + "login": "lalitkumawat1m", + "id": 91591901, + "avatar_url": "https://avatars.githubusercontent.com/u/91591901?v=4", + "html_url": "https://github.com/lalitkumawat1m" + }, + { + "login": "JamesNK", + "id": 303201, + "avatar_url": "https://avatars.githubusercontent.com/u/303201?v=4", + "html_url": "https://github.com/JamesNK" + }, + { + "login": "GabenGar", + "id": 87906913, + "avatar_url": "https://avatars.githubusercontent.com/u/87906913?v=4", + "html_url": "https://github.com/GabenGar" + }, + { + "login": "Ali7040", + "id": 81979505, + "avatar_url": "https://avatars.githubusercontent.com/u/81979505?v=4", + "html_url": "https://github.com/Ali7040" + }, + { + "login": "Saumya40-codes", + "id": 115284013, + "avatar_url": "https://avatars.githubusercontent.com/u/115284013?v=4", + "html_url": "https://github.com/Saumya40-codes" + }, + { + "login": "prajjwalyd", + "id": 111794524, + "avatar_url": "https://avatars.githubusercontent.com/u/111794524?v=4", + "html_url": "https://github.com/prajjwalyd" + }, + { + "login": "praveen-rikhari", + "id": 84331681, + "avatar_url": "https://avatars.githubusercontent.com/u/84331681?v=4", + "html_url": "https://github.com/praveen-rikhari" + }, + { + "login": "Michael-Obele", + "id": 70345027, + "avatar_url": "https://avatars.githubusercontent.com/u/70345027?v=4", + "html_url": "https://github.com/Michael-Obele" + }, + { + "login": "michaelmior", + "id": 82501, + "avatar_url": "https://avatars.githubusercontent.com/u/82501?v=4", + "html_url": "https://github.com/michaelmior" + }, + { + "login": "Maykkkk", + "id": 91688667, + "avatar_url": "https://avatars.githubusercontent.com/u/91688667?v=4", + "html_url": "https://github.com/Maykkkk" + }, + { + "login": "dauinh", + "id": 62679553, + "avatar_url": "https://avatars.githubusercontent.com/u/62679553?v=4", + "html_url": "https://github.com/dauinh" + }, + { + "login": "kurtmckee", + "id": 39996, + "avatar_url": "https://avatars.githubusercontent.com/u/39996?v=4", + "html_url": "https://github.com/kurtmckee" + }, + { + "login": "kalivtrope", + "id": 30574738, + "avatar_url": "https://avatars.githubusercontent.com/u/30574738?v=4", + "html_url": "https://github.com/kalivtrope" + }, + { + "login": "KaranDasA1", + "id": 141057770, + "avatar_url": "https://avatars.githubusercontent.com/u/141057770?v=4", + "html_url": "https://github.com/KaranDasA1" + }, + { + "login": "smoya", + "id": 1083296, + "avatar_url": "https://avatars.githubusercontent.com/u/1083296?v=4", + "html_url": "https://github.com/smoya" + }, + { + "login": "SimonDMC", + "id": 46278840, + "avatar_url": "https://avatars.githubusercontent.com/u/46278840?v=4", + "html_url": "https://github.com/SimonDMC" + }, + { + "login": "ThomasAribart", + "id": 38014240, + "avatar_url": "https://avatars.githubusercontent.com/u/38014240?v=4", + "html_url": "https://github.com/ThomasAribart" + }, + { + "login": "Viicos", + "id": 65306057, + "avatar_url": "https://avatars.githubusercontent.com/u/65306057?v=4", + "html_url": "https://github.com/Viicos" + }, + { + "login": "MeastroZI", + "id": 106718914, + "avatar_url": "https://avatars.githubusercontent.com/u/106718914?v=4", + "html_url": "https://github.com/MeastroZI" + }, + { + "login": "Mvishal123", + "id": 113995907, + "avatar_url": "https://avatars.githubusercontent.com/u/113995907?v=4", + "html_url": "https://github.com/Mvishal123" + }, + { + "login": "ota-meshi", + "id": 16508807, + "avatar_url": "https://avatars.githubusercontent.com/u/16508807?v=4", + "html_url": "https://github.com/ota-meshi" + }, + { + "login": "albinstman", + "id": 24735159, + "avatar_url": "https://avatars.githubusercontent.com/u/24735159?v=4", + "html_url": "https://github.com/albinstman" + }, + { + "login": "fevrin", + "id": 5051062, + "avatar_url": "https://avatars.githubusercontent.com/u/5051062?v=4", + "html_url": "https://github.com/fevrin" + }, + { + "login": "its-VinayKumar", + "id": 129538020, + "avatar_url": "https://avatars.githubusercontent.com/u/129538020?v=4", + "html_url": "https://github.com/its-VinayKumar" + }, + { + "login": "kaptinlin", + "id": 999331, + "avatar_url": "https://avatars.githubusercontent.com/u/999331?v=4", + "html_url": "https://github.com/kaptinlin" + }, + { + "login": "abhishek-403", + "id": 131962113, + "avatar_url": "https://avatars.githubusercontent.com/u/131962113?v=4", + "html_url": "https://github.com/abhishek-403" + }, + { + "login": "AdityaSingh-02", + "id": 94185167, + "avatar_url": "https://avatars.githubusercontent.com/u/94185167?v=4", + "html_url": "https://github.com/AdityaSingh-02" + }, + { + "login": "Akshaybagai52", + "id": 100753870, + "avatar_url": "https://avatars.githubusercontent.com/u/100753870?v=4", + "html_url": "https://github.com/Akshaybagai52" + }, + { + "login": "unguul", + "id": 7005614, + "avatar_url": "https://avatars.githubusercontent.com/u/7005614?v=4", + "html_url": "https://github.com/unguul" + }, + { + "login": "AymarN", + "id": 35435075, + "avatar_url": "https://avatars.githubusercontent.com/u/35435075?v=4", + "html_url": "https://github.com/AymarN" + }, + { + "login": "iamAyushChamoli", + "id": 75367725, + "avatar_url": "https://avatars.githubusercontent.com/u/75367725?v=4", + "html_url": "https://github.com/iamAyushChamoli" + }, + { + "login": "bhavukkalra", + "id": 41142068, + "avatar_url": "https://avatars.githubusercontent.com/u/41142068?v=4", + "html_url": "https://github.com/bhavukkalra" + }, + { + "login": "big-andy-coates", + "id": 8012398, + "avatar_url": "https://avatars.githubusercontent.com/u/8012398?v=4", + "html_url": "https://github.com/big-andy-coates" + }, + { + "login": "bcherny", + "id": 1761758, + "avatar_url": "https://avatars.githubusercontent.com/u/1761758?v=4", + "html_url": "https://github.com/bcherny" + }, + { + "login": "CarstenWickner", + "id": 11309681, + "avatar_url": "https://avatars.githubusercontent.com/u/11309681?v=4", + "html_url": "https://github.com/CarstenWickner" + }, + { + "login": "AdrianGonz97", + "id": 31664583, + "avatar_url": "https://avatars.githubusercontent.com/u/31664583?v=4", + "html_url": "https://github.com/AdrianGonz97" + }, + { + "login": "kx412764776", + "id": 117196993, + "avatar_url": "https://avatars.githubusercontent.com/u/117196993?v=4", + "html_url": "https://github.com/kx412764776" + }, + { + "login": "rotu", + "id": 119948, + "avatar_url": "https://avatars.githubusercontent.com/u/119948?v=4", + "html_url": "https://github.com/rotu" + }, + { + "login": "danielaparker", + "id": 4381912, + "avatar_url": "https://avatars.githubusercontent.com/u/4381912?v=4", + "html_url": "https://github.com/danielaparker" + }, + { + "login": "theory", + "id": 46604, + "avatar_url": "https://avatars.githubusercontent.com/u/46604?v=4", + "html_url": "https://github.com/theory" + }, + { + "login": "vm-001", + "id": 20838671, + "avatar_url": "https://avatars.githubusercontent.com/u/20838671?v=4", + "html_url": "https://github.com/vm-001" + }, + { + "login": "eddyashton", + "id": 6000239, + "avatar_url": "https://avatars.githubusercontent.com/u/6000239?v=4", + "html_url": "https://github.com/eddyashton" + }, + { + "login": "elisiariocouto", + "id": 818914, + "avatar_url": "https://avatars.githubusercontent.com/u/818914?v=4", + "html_url": "https://github.com/elisiariocouto" + }, + { + "login": "1p22geo", + "id": 113300767, + "avatar_url": "https://avatars.githubusercontent.com/u/113300767?v=4", + "html_url": "https://github.com/1p22geo" + }, + { + "login": "joostholslag", + "id": 13568972, + "avatar_url": "https://avatars.githubusercontent.com/u/13568972?v=4", + "html_url": "https://github.com/joostholslag" + }, + { + "login": "Nickyshe", + "id": 110067624, + "avatar_url": "https://avatars.githubusercontent.com/u/110067624?v=4", + "html_url": "https://github.com/Nickyshe" + }, + { + "login": "justin-tay", + "id": 49700559, + "avatar_url": "https://avatars.githubusercontent.com/u/49700559?v=4", + "html_url": "https://github.com/justin-tay" + } +] \ No newline at end of file diff --git a/components/Accordion.tsx b/components/Accordion.tsx index 02b50ab2b..d6428ff0d 100644 --- a/components/Accordion.tsx +++ b/components/Accordion.tsx @@ -51,10 +51,11 @@ const Accordion: React.FC = ({ items }) => {
{items.map((item, index) => (
@@ -64,6 +65,7 @@ const Accordion: React.FC = ({ items }) => { e.preventDefault(); handleLinkClick(item.id); }} + data-test={`accordion-question-${item.id}`} > {item.question} @@ -73,6 +75,7 @@ const Accordion: React.FC = ({ items }) => { activeIndex === index ? 'rotate-45' : '' }`} onClick={() => handleToggle(index)} + data-test={`accordion-toggle-${item.id}`} > +
@@ -81,6 +84,7 @@ const Accordion: React.FC = ({ items }) => {
{item.answer}
diff --git a/components/Code.tsx b/components/Code.tsx index c10c49c7c..7460fa4e8 100644 --- a/components/Code.tsx +++ b/components/Code.tsx @@ -12,6 +12,7 @@ export default function Code({ children }: { children: any }) { 'bg-amber-200': blockContext === BlockContextValue.Information, 'text-white': blockContext === BlockContextValue.CodeBlock, })} + data-test='code' > {children} diff --git a/components/DarkModeToggle.tsx b/components/DarkModeToggle.tsx index d45169780..798a92646 100644 --- a/components/DarkModeToggle.tsx +++ b/components/DarkModeToggle.tsx @@ -5,14 +5,17 @@ import React from 'react'; function ListItem({ children, onClick, + 'data-test': dataTest, }: { children: React.ReactNode; onClick: () => void; + 'data-test'?: string; }) { return (
{children}
@@ -28,7 +31,9 @@ export default function DarkModeToggle() { }, [resolvedTheme]); const [showSelect, setShowSelect] = useState(false); - const [activeThemeIcon, setActiveThemeIcon] = useState(''); + const [activeThemeIcon, setActiveThemeIcon] = useState( + '/icons/theme-switch.svg', + ); useEffect(() => { switch (theme) { case 'system': @@ -45,6 +50,7 @@ export default function DarkModeToggle() {
- setTheme('system')}> + setTheme('system')} + data-test='select-system-theme' + > System theme System - setTheme('light')}> + setTheme('light')} + data-test='select-light-theme' + > System theme Light - setTheme('dark')}> + setTheme('dark')} + data-test='select-dark-theme' + > System theme -

Need Help?

+
+

+ Need Help? +

-

+

Did you find these docs helpful?

@@ -85,6 +101,7 @@ export function DocsHelp({ markdownFile }: DocsHelpProps) { className='flex flex-col' onSubmit={createFeedbackHandler} ref={feedbackFormRef} + data-test='feedback-form' >
{ setIsFormOpen(true); }} + data-test='feedback-survey-yes-button' > { setIsFormOpen(true); }} + data-test='feedback-survey-no-button' >
-

+

@@ -173,6 +193,7 @@ export function DocsHelp({ markdownFile }: DocsHelpProps) { type='submit' className={`px-[16px] py-[7px] cursor-pointer border-solid border-[#aaaaaa] border rounded-md ${isSubmitting ? 'cursor-not-allowed opacity-50' : 'hover:bg-gray-200 dark:hover:bg-gray-600'}`} disabled={isSubmitting} + data-test='feedback-submit-button' > -

+

Thanks for the feedback! Feel free to join the  -

+

Thanks for creating an issue! Let's continue the discussion there!

@@ -255,18 +277,21 @@ export function DocsHelp({ markdownFile }: DocsHelpProps) { {error && (
-

{error}

+

{error}

)}
-

+

Help us make our docs great!

-

+

At JSON Schema, we value docs contributions as much as every other type of contribution!

@@ -277,6 +302,7 @@ export function DocsHelp({ markdownFile }: DocsHelpProps) { rel='noreferrer' className='px-[16px] py-[8px] cursor-pointer border-solid border-[#aaaaaa] border rounded-md hover:bg-gray-200 dark:hover:bg-gray-600' href={`https://github.com/json-schema-org/website/blob/main/pages${markdownFile ? (markdownFile === '_indexPage' ? extractPathWithoutFragment(router.asPath) + '/_index.md' : extractPathWithoutFragment(router.asPath) + '.md') : `/${path}/index.page.tsx`}`} + data-test='edit-on-github-link' >
-

Still Need Help?

+

+ Still Need Help? +

-

+

Learning JSON Schema is often confusing, but don't worry, we are here to help!.

@@ -333,6 +365,7 @@ export function DocsHelp({ markdownFile }: DocsHelpProps) { rel='noreferrer' className='underline' href='https://github.com/orgs/json-schema-org/discussions/new?category=q-a' + data-test='ask-on-github-link' > {.*}).*\n/g; // Prevent annoying error messages because slate is not SSR ready +/* istanbul ignore next: + * This code is used to prevent an error message that occurs when running in a non-browser + * environment. So ignoring this code in the code coverage reports. + */ if (typeof process !== 'undefined' && process.browser !== true) { React.useLayoutEffect = React.useEffect; } @@ -84,6 +88,7 @@ const getTextPathIndexesFromNode = ( } const customNode = customElement as CustomNode; const textPathIndexesFromNodes = getTextPathIndexesFromNodes( + /* istanbul ignore next: customNode?.children cannot be null */ customNode?.children || [], path, acc, @@ -172,6 +177,7 @@ const deserializeCode = (code: string): CustomElement[] => { export default function JsonEditor({ initialCode }: { initialCode: string }) { const fullMarkdown = useContext(FullMarkdownContext); + /* istanbul ignore next: In the test environment, the fullMarkdown is not provided. */ const hasCodeblockAsDescendant: boolean | undefined = (() => { const positionOfCodeInFullMarkdown = fullMarkdown?.indexOf(initialCode); if (!positionOfCodeInFullMarkdown) return; @@ -204,8 +210,10 @@ export default function JsonEditor({ initialCode }: { initialCode: string }) { if (!metaRegexFinding) return null; try { const metaString: undefined | string = metaRegexFinding?.groups?.meta; + /* istanbul ignore next: metaString cannot be null if the regex matched */ if (!metaString) return null; const meta = JSON.parse(metaString); + /* istanbul ignore next: meta cannot be null if the regex matched */ return meta || null; } catch (e) { return null; @@ -239,6 +247,7 @@ export default function JsonEditor({ initialCode }: { initialCode: string }) { // fullCodeText variable is for use in copy pasting the code for the user const fullCodeText = React.useMemo(() => { let text = ''; + /* istanbul ignore else : there is no else block to test here */ if (value) { value.forEach((e: any) => { text += e.children[0].text + '\n'; @@ -259,7 +268,12 @@ export default function JsonEditor({ initialCode }: { initialCode: string }) { setValue(e)} + onChange={ + /* istanbul ignore next: + * Editor is read-only, so the onChange function will never be called. + */ + (e) => setValue(e) + } >
setCopied(false), 2000); }} + data-test='copy-clipboard-button' >
-
+
{isJsonSchema ? ( <> schema @@ -301,21 +319,27 @@ export default function JsonEditor({ initialCode }: { initialCode: string }) {
{ e.preventDefault(); const text = window.getSelection()?.toString(); navigator.clipboard.writeText(text || ''); }} - onCut={(e) => { - e.preventDefault(); - const text = window.getSelection()?.toString(); - navigator.clipboard.writeText(text || ''); - setValue([{ type: 'paragraph', children: [{ text: '' }] }]); - }} + onCut={ + /* istanbul ignore next : + The editor is read-only, so the onCut function will never be called. */ + (e) => { + e.preventDefault(); + const text = window.getSelection()?.toString(); + navigator.clipboard.writeText(text || ''); + setValue([{ type: 'paragraph', children: [{ text: '' }] }]); + } + } readOnly={true} decorate={([node, path]) => { if (!Text.isText(node)) return []; const stringPath = path.join(','); + /* istanbul ignore next: allPathDecorationsMap[stringPath] cannot be null */ return allPathDecorationsMap[stringPath] || []; }} renderLeaf={(props: any) => { @@ -382,6 +406,7 @@ export default function JsonEditor({ initialCode }: { initialCode: string }) { return ( { + /* istanbul ignore if : link cannot be null */ if (!link) return; router.push(link); }} @@ -395,10 +420,10 @@ export default function JsonEditor({ initialCode }: { initialCode: string }) { }} renderElement={(props: any) => { // This will be the path to the image element. - const { element, children, attributes } = props; const path = ReactEditor.findPath(editor, element); const line = path[0] + 1; + /* istanbul ignore else : no else block to test */ if (element.type === 'paragraph') { return ( ); } + /* istanbul ignore next: + * There is no other element type in the render function. Hence this will never be called.*/ throw new Error( `unknown element.type [${element.type}] in render function`, ); @@ -420,13 +447,19 @@ export default function JsonEditor({ initialCode }: { initialCode: string }) { /> {validation === 'invalid' && ( -
+
not compliant to schema
)} {validation === 'valid' && ( -
+
compliant to schema
@@ -436,6 +469,7 @@ export default function JsonEditor({ initialCode }: { initialCode: string }) { className={classnames('text-center text-xs pt-2 text-slate-400', { 'mb-10': !hasCodeblockAsDescendant, })} + data-test='code-caption' > {caption}
diff --git a/components/Remember.tsx b/components/Remember.tsx index d1ea0aa25..eaa5305ea 100644 --- a/components/Remember.tsx +++ b/components/Remember.tsx @@ -3,7 +3,10 @@ import React from 'react'; export const Remember = () => { return (
-

+

{ {' '} Remember

- + Contribute to the JSON Schema Docs -
+
Code isn't the only way to contribute to OSS; Docs are extremely import for the JSON Schema Ecosystem. At JSON Schema, We value Docs contributions as much as every other type of contribution!
- + To get started as a Docs contributor:
@@ -70,10 +79,13 @@ export const Remember = () => {
- + Docs contributor questions? -
+
Do you have a documentation contributor question? Please leave a comment in the issue or PR or join the #contribute or{' '} #documentation channels on{' '} diff --git a/components/ScrollButton.tsx b/components/ScrollButton.tsx index 513868d38..b02355069 100644 --- a/components/ScrollButton.tsx +++ b/components/ScrollButton.tsx @@ -14,6 +14,7 @@ export default function ScrollButton() { window.addEventListener('scroll', handleScroll); // Cleanup function to remove the event listener when the component unmounts + /* istanbul ignore next : can't test cleanup function */ return () => window.removeEventListener('scroll', handleScroll); }, []); @@ -31,6 +32,7 @@ export default function ScrollButton() { onClick={scrollUp} className='rounded-full transition-transform hover:-translate-y-2 duration-150 ease-in-out shadow-lg bg-white flex items-center justify-center' aria-label='Scroll to top' + data-test='scroll-button' > {/* Ensure the image is in your public/img directory */} + > = { '"string"': '/understanding-json-schema/reference/string', '"object"': '/understanding-json-schema/reference/object', '"number"': '/understanding-json-schema/reference/numeric#number', + '"array"': '/understanding-json-schema/reference/array', + '"boolean"': '/understanding-json-schema/reference/boolean', }, }; diff --git a/cypress/components/Accordian.cy.tsx b/cypress/components/Accordian.cy.tsx new file mode 100644 index 000000000..5e54db05f --- /dev/null +++ b/cypress/components/Accordian.cy.tsx @@ -0,0 +1,189 @@ +import React from 'react'; +import Accordion from '~/components/Accordion'; +import mockNextRouter, { MockRouter } from '../plugins/mockNextRouterUtils'; + +interface AccordionItem { + question: string; + answer: string; + id: number; +} + +interface AccordionProps { + items: AccordionItem[]; +} + +const items: AccordionProps['items'] = [ + { + question: 'What is JSON Schema?', + answer: + 'JSON Schema is a vocabulary that allows you to annotate and validate JSON documents. It is used to define the structure of JSON data for documentation, validation, and interaction control.', + id: 1, + }, + { + question: 'What is JSON Schema used for?', + answer: + 'JSON Schema is used to define the structure of JSON data for documentation, validation, and interaction control.', + id: 2, + }, + { + question: 'What is JSON Schema validation?', + answer: + 'JSON Schema validation is the process of ensuring that a JSON document is correctly formatted and structured according to a JSON Schema.', + id: 3, + }, +]; + +describe('Accordion Component', () => { + let mockRouter: MockRouter; + beforeEach(() => { + mockRouter = mockNextRouter(); + cy.mount(); + }); + + // Render the Accordion component with items corrently + it('should render the Accordion Items correctly', () => { + // Check if all items are rendered + items.forEach((item) => { + cy.get(`[data-test="accordion-item-${item.id}"]`) + .should('exist') + .within(() => { + // Check if the question is rendered + cy.get(`[data-test=accordion-question-${item.id}]`).should( + 'have.text', + item.question, + ); + + // Now click on the question to see the answer + cy.get(`[data-test=accordion-question-${item.id}]`).click(); + cy.get('@routerPush').should('have.been.calledWith', `#${item.id}`); + cy.get(`[ data-test=accordion-answer-${item.id}]`).should( + 'have.text', + item.answer, + ); + }); + // Check if answer is expanded and visible + cy.get(`[data-test=accordion-item-${item.id}]`).should( + 'have.class', + 'max-h-96', + ); + }); + }); + + // Toggle Button of the Accordion items should work correctly + it('should handle the toggle correctly', () => { + /* Testing the behavior with the first item */ + + // Initially, all items should be collapsed + cy.get(`[data-test="accordion-item-${items[0].id}"]`).should( + 'have.class', + 'max-h-20', + ); + + // Click on the first item to expand it + cy.get(`[data-test="accordion-toggle-${items[0].id}"]`).click(); + + // Check if the first item is expanded + cy.get(`[data-test="accordion-item-${items[0].id}"]`).should( + 'have.class', + 'max-h-96', + ); + + // Click on the toggle button to collapse the first item + cy.get(`[data-test="accordion-toggle-${items[0].id}"]`).click(); + + // Check if the first item is collapsed + cy.get(`[data-test="accordion-item-${items[0].id}"]`).should( + 'have.class', + 'max-h-20', + ); + }); + + describe('Accordion scrolling behavior', () => { + /* Testing the behavior with the first item */ + + // Scroll to the Accordion item when the router asPath changes + it('should scroll when router asPath changes', () => { + // spy the scrollTo method + const scrollToSpy = cy.spy(window, 'scrollTo'); + const foundIndex = 0; + cy.get(`[data-test="accordion-item-${items[0].id}"]`).should( + 'have.class', + 'max-h-20', + ); + cy.get(`[data-test="accordion-question-${items[0].id}"]`).click(); + + // Simulate the behavior of findIndex + cy.stub(items, 'findIndex').callsFake((predicate) => { + console.log(predicate); + return predicate({ id: items[0].id }) ? foundIndex : -1; + }); + + // Simulate the router asPath change + mockRouter.asPath = `#${items[0].id}`; + cy.wrap(null).then(() => { + Cypress.$(window).trigger('hashchange'); + }); + + // Check if the accordion item is expanded + cy.get(`[data-test="accordion-item-${items[0].id}"]`).should( + 'have.class', + 'max-h-96', + ); + + // Check if scrollTo was called + cy.wrap(scrollToSpy).should('have.been.called'); + cy.get(`[data-test="accordion-answer-${items[0].id}"]`).should('exist'); + + // Check if scrollTo was called with the correct arguments + cy.wrap(scrollToSpy).should('have.been.calledWithMatch', { + top: Cypress.sinon.match.number, + behavior: 'smooth', + }); + }); + + it('should not scroll when findIndex is -1', () => { + /* Testing the behavior with the first item */ + + const foundIndex = 0; + cy.get(`[data-test="accordion-question-${items[0].id}"]`).click(); + + // Simulate the router asPath change to the first item + mockRouter.asPath = `#${items[0].id}`; + cy.wrap(null).then(() => { + Cypress.$(window).trigger('hashchange'); + }); + + // Using mockId as null to get findIndex return -1 + cy.stub(items, 'findIndex').callsFake((predicate) => { + console.log(predicate); + return predicate({ id: null }) ? foundIndex : -1; + }); + }); + + it('should not scroll when element is not found', () => { + /* Testing the behavior with the first item */ + + const foundIndex = 0; + // Stub the getElementById method + const getElementByIdStub = cy.stub(document, 'getElementById'); + + cy.get(`[data-test="accordion-question-${items[0].id}"]`).click(); + + // Simulate the router asPath change to the first item + mockRouter.asPath = `#${items[0].id}`; + cy.wrap(null).then(() => { + Cypress.$(window).trigger('hashchange'); + }); + + // Simulate the behavior of findIndex when element not found + cy.stub(items, 'findIndex').callsFake((predicate) => { + return predicate({ id: items[0].id }) ? foundIndex : -1; + }); + + // using mockId as -1 to simulate the behavior of getElementById when element not found + const mockId = -1; + const mockElement = document.createElement('div'); + getElementByIdStub.withArgs(mockId.toString()).returns(mockElement); + }); + }); +}); diff --git a/cypress/components/Code.cy.tsx b/cypress/components/Code.cy.tsx new file mode 100644 index 000000000..e6b872e0c --- /dev/null +++ b/cypress/components/Code.cy.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import Code from '~/components/Code'; + +describe('Code Component', () => { + const testCodeRendering = (props: { children: string }) => { + cy.mount(); + cy.get('[data-test="code"]').should('have.text', props.children); + }; + + it('should render code correctly', () => { + testCodeRendering({ children: 'const foo = "bar";' }); + }); +}); diff --git a/cypress/components/DarkModeToggle.cy.tsx b/cypress/components/DarkModeToggle.cy.tsx new file mode 100644 index 000000000..839e9a06b --- /dev/null +++ b/cypress/components/DarkModeToggle.cy.tsx @@ -0,0 +1,120 @@ +import React from 'react'; +import DarkModeToggle from '~/components/DarkModeToggle'; + +import { ThemeProvider } from 'next-themes'; + +describe('DarkModeToggle Component', () => { + const TOGGLE_BUTTON = '[data-test="dark-mode-toggle"]'; + const THEME_DROPDOWN = '[data-test="theme-dropdown"]'; + const THEME_ICON = '[data-test="theme-icon"]'; + + const mountComponent = () => { + cy.mount( +
+ + + +
, + ); + }; + + beforeEach(mountComponent); + + // Should render the component + it('should render the component', () => { + // check if the toggle button exists + cy.get(TOGGLE_BUTTON).should('exist'); + }); + + // Test Theme Dropdown Menu Functionality + describe('Theme Dropdown Menu', () => { + // Should close the menu on button click when open + it('should open and close the menu on button click', () => { + // click on the toggle button to open the menu + cy.get(TOGGLE_BUTTON).click(); + + // check if the menu is open + cy.get(THEME_DROPDOWN).should('have.css', 'display', 'block'); + + // click on the toggle button again to close the menu + cy.get(TOGGLE_BUTTON).click(); + + // check if the menu is closed + cy.get(THEME_DROPDOWN).should('have.css', 'display', 'none'); + }); + + // Should close the menu on mouse leave + it('should close the menu on mouse leave', () => { + // click on the toggle button to open the menu + cy.get(TOGGLE_BUTTON).click(); + + // check if the menu is open + cy.get(THEME_DROPDOWN).should('have.css', 'display', 'block'); + + // trigger mouse leave event on the menu + cy.get(THEME_DROPDOWN).trigger('mouseout'); + + // check if the menu is closed + cy.get(THEME_DROPDOWN).should('have.css', 'display', 'none'); + }); + }); + + // Test Theme Selection Functionality + describe('Theme Selection', () => { + const themes = [ + { + name: 'system', + selector: '[data-test="select-system-theme"]', + icon: '/icons/theme-switch.svg', + }, + { + name: 'dark', + selector: '[data-test="select-dark-theme"]', + icon: '/icons/moon.svg', + }, + { + name: 'light', + selector: '[data-test="select-light-theme"]', + icon: '/icons/sun.svg', + }, + ]; + + // Iterate over each theme and test if the theme changes + themes.forEach((theme) => { + it(`should change the theme to ${theme.name}`, () => { + // click on the toggle button to open the menu + cy.get(TOGGLE_BUTTON).click(); + + // check if the menu is open + cy.get(THEME_DROPDOWN).should('have.css', 'display', 'block'); + + // click on the theme option + cy.get(theme.selector).click(); + + // check if the theme icon has changed + cy.get(THEME_ICON).should('have.attr', 'src', theme.icon); + + // trigger mouse leave event on the menu + cy.get(THEME_DROPDOWN).trigger('mouseout'); + + // check if the menu is closed + cy.get(THEME_DROPDOWN).should('have.css', 'display', 'none'); + }); + }); + + // Initial theme should be system theme + it('should initially have system theme', () => { + // check if the theme icon is the system theme icon + cy.get(THEME_ICON).should('have.attr', 'src', '/icons/theme-switch.svg'); + }); + }); +}); diff --git a/cypress/components/DocsHelp.cy.tsx b/cypress/components/DocsHelp.cy.tsx new file mode 100644 index 000000000..cfd9acc80 --- /dev/null +++ b/cypress/components/DocsHelp.cy.tsx @@ -0,0 +1,251 @@ +import React from 'react'; +import { DocsHelp } from '~/components/DocsHelp'; +import mockNextRouter, { MockRouter } from '../plugins/mockNextRouterUtils'; + +// DocsHelp Data Test IDs +const DOCS_HELP = '[data-test="docs-help"]'; +const FEEDBACK_FORM = '[data-test="feedback-form"]'; +const FEEDBACK_FORM_YES_BUTTON = '[data-test="feedback-survey-yes-button"]'; +const FEEDBACK_FORM_NO_BUTTON = '[data-test="feedback-survey-no-button"]'; +const FEEDBACK_FORM_INPUT = '[data-test="feedback-form-input"]'; +const FEEDBACK_FORM_SUBMIT_BUTTON = '[data-test="feedback-submit-button"]'; +const CREATE_GITHUB_ISSUE_BUTTON = '[data-test="create-github-issue-button"]'; +const FEEDBACK_FORM_SUCCESS_MESSAGE = + '[data-test="feedback-form-success-message"]'; +const FEEDBACK_ERROR_MESSAGE = '[data-test="feedback-form-error-message"]'; +const FEEDBACK_FORM_GITHUB_SUCCESS_MESSAGE = + '[data-test="feedback-form-github-success-message"]'; + +// DocsHelp Component +describe('DocsHelp Component', () => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + let mockRouter: MockRouter; + // Note: we are not using the mockRouter in this test file, but it is required to mock the router in the component file + + beforeEach(() => { + const markdownFile = '_index'; + mockRouter = mockNextRouter(); + cy.viewport(1200, 800); + cy.mount(); + }); + + // should render the component correctly + it('should render the component correctly', () => { + // Check if the main component wrapper is present + cy.get(DOCS_HELP).should('exist'); + + // "Need Help?" header + cy.get('[data-test="need-help-heading"]') + .should('have.prop', 'tagName', 'H2') + .and('contains', /Need Help?/i); + + // Main feedback question + cy.get('[data-test="feedback-main-heading"]') + .should('have.prop', 'tagName', 'H3') + .and('contains', /Did you find these docs helpful?/i); + + // Feedback form element + cy.get(FEEDBACK_FORM).should('have.prop', 'tagName', 'FORM'); + + // "Help us improve" section header + cy.get('[data-test="contribute-docs-heading"]') + .should('have.prop', 'tagName', 'H3') + .and('contains', /Help us make our docs great!/i); + + // Contribution encouragement text + cy.get('[data-test="contribute-docs-description"]') + .should('have.prop', 'tagName', 'P') + .and( + 'contains', + /At JSON Schema, we value docs contributions as much as every other type of contribution!/i, + ); + + // "Edit on GitHub" link + cy.get('[data-test="edit-on-github-link"]') + .should('have.prop', 'tagName', 'A') + .and('contains', /Edit this page on Github/i); + + // "Learn to contribute" link + cy.get('[data-test="learn-to-contribute-link"]') + .should('have.prop', 'tagName', 'A') + .and( + 'have.attr', + 'href', + 'https://github.com/json-schema-org/website/blob/main/CONTRIBUTING.md', + ) + .and('contains', /Learn how to contribute/i); + + // "Still Need Help?" section header + cy.get('[data-test="additional-help-heading"]') + .should('have.prop', 'tagName', 'H3') + .and('contains', /Still Need Help?/i); + + // Additional help description + cy.get('[data-test="additional-help-description"]') + .should('have.prop', 'tagName', 'P') + .should( + 'contains', + /Learning JSON Schema is often confusing, but don't worry, we are here to help!./i, + ); + + // GitHub community link + cy.get('[ data-test="ask-on-github-link"]') + .should('have.prop', 'tagName', 'A') + .and( + 'have.attr', + 'href', + 'https://github.com/orgs/json-schema-org/discussions/new?category=q-a', + ) + .and('contains', /Ask the community on GitHub/i); + + // Slack community link + cy.get('[data-test="ask-on-slack-link"]') + .should('have.prop', 'tagName', 'A') + .and('have.attr', 'href', 'https://json-schema.org/slack') + .and('contains', /Ask the community on Slack/i); + }); + + // test feedback form funtionality works correctly + it('should handle successful feedback submission', () => { + // mocking the feedback api call + cy.intercept( + 'POST', + 'https://script.google.com/macros/s/AKfycbx9KA_BwTdsYgOfTLrHAxuhHs_wgYibB5_Msj9XP1rL5Ip4A20g1O609xAuTZmnbhRv/exec', + { + statusCode: 200, + body: { success: true }, + }, + ).as('feedback'); + + /* click on yes button and check if feedback form is visible + Note: checking both yes and no button to cover both scenarios */ + cy.get(FEEDBACK_FORM_YES_BUTTON).click(); + cy.get(FEEDBACK_FORM_NO_BUTTON).click(); + cy.get(FEEDBACK_FORM).should('be.visible'); + + // now type in feedback form and submit + cy.get(FEEDBACK_FORM_INPUT).type('JSON Schema is awesome'); + cy.get(FEEDBACK_FORM_SUBMIT_BUTTON).click(); + + // check if response status code is 200 + cy.wait('@feedback').its('response.statusCode').should('eq', 200); + + // check if clicking on submit button should show success message + cy.get(FEEDBACK_FORM_SUCCESS_MESSAGE) + .should('have.prop', 'tagName', 'P') + .and('contains', /Thank you for your feedback!/i); + }); + + /* test feedback form functionality when status code is 500 + Note: This is case when server returns an error response | eg: INTERNAL SERVER ERROR */ + it('should handle API error response', () => { + // check if clicking on yes button should show feedback form + cy.intercept( + 'POST', + 'https://script.google.com/macros/s/AKfycbx9KA_BwTdsYgOfTLrHAxuhHs_wgYibB5_Msj9XP1rL5Ip4A20g1O609xAuTZmnbhRv/exec', + { + statusCode: 500, + body: { error: 'Internal Server Error' }, + }, + ).as('feedback'); + + // click on yes button and check if feedback form is visible + cy.get(FEEDBACK_FORM_YES_BUTTON).click(); + cy.get(FEEDBACK_FORM).should('be.visible'); + + // now type in feedback form and submit + cy.get(FEEDBACK_FORM_INPUT).type('JSON Schema is awesome'); + cy.get(FEEDBACK_FORM_SUBMIT_BUTTON).click(); + + // check if response status code is 500 + cy.wait('@feedback').its('response.statusCode').should('eq', 500); + + // check if clicking on submit button should show error message + cy.get(FEEDBACK_ERROR_MESSAGE) + .should('have.prop', 'tagName', 'P') + .and('contains', /An error occurred. Please try again later./i); + }); + + /* test feedback form functionality when network error occurs + Note: This is case when network error occurs while sending request to server | eg: NO INTERNET CONNECTION */ + it('should handle network error', () => { + // check if clicking on yes button should show feedback form + cy.intercept( + 'POST', + 'https://script.google.com/macros/s/AKfycbx9KA_BwTdsYgOfTLrHAxuhHs_wgYibB5_Msj9XP1rL5Ip4A20g1O609xAuTZmnbhRv/exec', + { + forceNetworkError: true, + }, + ).as('feedback'); + + // click on yes button and check if feedback form is visible + cy.get(FEEDBACK_FORM_YES_BUTTON).click(); + cy.get(FEEDBACK_FORM).should('be.visible'); + + // now type in feedback form and submit + cy.get(FEEDBACK_FORM_INPUT).type('JSON Schema is awesome'); + cy.get(FEEDBACK_FORM_SUBMIT_BUTTON).click(); + + // check if clicking on submit button should show error message + cy.get(FEEDBACK_ERROR_MESSAGE) + .should('have.prop', 'tagName', 'P') + .and('contains', /An error occurred. Please try again later./i); + }); + + // test create github issue functionality when submitting feedback + it('should open github issue page', () => { + // mock window.open function + cy.window().then((win) => { + cy.stub(win, 'open').as('windowOpen'); + }); + // check if clicking on yes button should show feedback form + cy.get(FEEDBACK_FORM_YES_BUTTON).click(); + cy.get(FEEDBACK_FORM).should('be.visible'); + + // now type in feedback form and submit + cy.get(FEEDBACK_FORM_INPUT).type('JSON Schema is awesome'); + cy.get(CREATE_GITHUB_ISSUE_BUTTON).click(); + + // check if clicking on submit button should show success message + cy.get(FEEDBACK_FORM_GITHUB_SUCCESS_MESSAGE) + .should('have.prop', 'tagName', 'P') + .and( + 'contains', + /Thanks for creating an issue! Let's continue the discussion there!/i, + ); + }); + + // test show error message when github issue page fails to open + it('should handle error while opening github issue page', () => { + // mock window.open function with error + cy.window().then((win) => { + cy.stub(win, 'open').throws(new Error('Test error')); + }); + + // check if clicking on yes button should show feedback form + cy.get(FEEDBACK_FORM_YES_BUTTON).click(); + cy.get(FEEDBACK_FORM).should('be.visible'); + + // now type in feedback form and submit + cy.get(FEEDBACK_FORM_INPUT).type('JSON Schema is awesome'); + cy.get(CREATE_GITHUB_ISSUE_BUTTON).click(); + + // check if clicking on submit button should show error message + cy.get(FEEDBACK_ERROR_MESSAGE) + .should('have.prop', 'tagName', 'P') + .and('contains', /An error occurred. Please try again later./i); + }); + + // This test is to check component render correctly with different markdown files + it('should render component with different markdown files', () => { + /* Note: Already checking with _index markdown file in the first test case */ + + // render with _indexPage markdown file + const markdownFile = '_indexPage'; + cy.mount(); + cy.get(DOCS_HELP).should('exist'); + + // render without any markdown file + cy.mount(); + }); +}); diff --git a/cypress/components/Faq.cy.tsx b/cypress/components/Faq.cy.tsx new file mode 100644 index 000000000..244c93ebe --- /dev/null +++ b/cypress/components/Faq.cy.tsx @@ -0,0 +1,78 @@ +import React from 'react'; +import Faq from '~/components/Faq'; +import mockNextRouter, { MockRouter } from '../plugins/mockNextRouterUtils'; +import faqData from '~/data/faq.json'; + +interface FaqData { + id: number; + question: string; + answer: string; + category: string; +} + +const mockFaqData: FaqData[] = [ + { + id: 1, + question: 'What is JSON Schema?', + answer: + 'JSON Schema is a specification for defining the structure of JSON data. It provides a standardized way to annotate, describe, and validate JSON data. JSON Schema defines content, structure, data types, and other expected constraints within a JSON document.', + category: 'fundamentals', + }, + { + id: 2, + question: 'How do I create a simple JSON Schema?', + answer: + 'You can create a simple JSON Schema using a JSON object with properties like "type", "properties", and "required". These define the data type, properties, and mandatory fields of your JSON document.', + category: 'implementation', + }, + { + id: 3, + question: 'What is the purpose of "type" in JSON Schema?', + answer: + 'The "type" keyword defines the data type of the schema and guarantees that the validated data complies with the specified type. JSON Schema defines these types; "string", "number", "integer", "object", "array", "boolean", and "null". The type keyword primarily functions for data validation, compatibility across systems, and restriction of JSON documents.', + category: 'validation', + }, + { + id: 4, + question: 'How can I validate a JSON document against a JSON Schema?', + answer: + 'You can use various tools and libraries, such as AJV (Another JSON Schema Validator), to validate a JSON document against a JSON Schema. It ensures the JSON data adheres to the expected format and constraints defined in the schema.', + category: 'tooling', + }, +]; + +describe('Faq Component', () => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + let mockRouter: MockRouter; + /* We are not using mockRouter here but it need here in order to run the test the component */ + + beforeEach(() => { + mockRouter = mockNextRouter(); + // mock the faqData filter method + cy.stub(faqData, 'filter').callsFake((predicate) => { + return mockFaqData.filter(predicate); + }); + }); + + /* Testing faq component with different categories */ + + // Should render the component with 'fundamentals' category + it('should render the component with fundamentals category', () => { + cy.mount(); + }); + + // Should render the component with 'implementation' category + it('should render the component with implementation category', () => { + cy.mount(); + }); + + // Should render the component with 'validation' category + it('should render the component with validation category', () => { + cy.mount(); + }); + + // Should render the component with 'tooling' category + it('should render the component with tooling category', () => { + cy.mount(); + }); +}); diff --git a/cypress/components/Headlines.cy.tsx b/cypress/components/Headlines.cy.tsx new file mode 100644 index 000000000..2fd627b5d --- /dev/null +++ b/cypress/components/Headlines.cy.tsx @@ -0,0 +1,108 @@ +import React from 'react'; +import { + Headline1, + Headline2, + Headline3, + Headline4, +} from '~/components/Headlines'; +import mockNextRouter, { MockRouter } from '../plugins/mockNextRouterUtils'; + +describe('Headlines Component', () => { + let mockRouter: MockRouter; + beforeEach(() => { + mockRouter = mockNextRouter(); + }); + + // Should render all types of headlines correctly with attributes and children + it('should render all types of headlines correctly with attributes and children', () => { + /*Testing all types of headline components with attributes and children and headlines should not be active*/ + + // Mount the Headline1 + cy.mount( + + Headline

1

+
, + ); + // Check if the headline is rendered correctly with the custom class + cy.get('h1') + .should('have.text', 'Headline 1') + .should('have.class', 'custom-class'); + // Check if headline is not active + cy.get('span').should('not.exist'); + + // Mount the Headline2 + cy.mount( + + Headline

2

+
, + ); + // Check if the headline is rendered correctly with the custom class + cy.get('h2') + .should('have.text', 'Headline 2') + .should('have.class', 'custom-class'); + // Check if headline is not active + cy.get('span').should('not.exist'); + + // Mount the Headline3 + cy.mount( + + Headline

3

+
, + ); + // Check if the headline is rendered correctly with the custom class + cy.get('h3') + .should('have.text', 'Headline 3') + .should('have.class', 'custom-class'); + // Check if headline is not active + cy.get('span').should('not.exist'); + + // Mount the Headline4 + cy.mount( + + Headline

4

+
, + ); + // Check if the headline is rendered correctly with the custom class + cy.get('h4') + .should('have.text', 'Headline 4') + .should('have.class', 'custom-class'); + // Check if headline is not active + cy.get('span').should('not.exist'); + }); + + // Check if the headline is active on click and the URL changes + it('should correctly call router push on click', () => { + /* Testing onClick property of headline with Headline1 */ + + // Mount the Headline1 + cy.mount(What is JSON Schema?); + // Click on the headline to make it active + cy.get('[data-test="headline"]').click(); + + // Push router to the URL with the hash + mockRouter.push('/#what-is-json-schema'); + + // Check if the headline is active + cy.get('span').should( + 'have.class', + 'text-startBlue dark:text-endBlue inline-block ml-2', + ); + cy.get('span').should('have.text', 'ยถ'); + }); + + // Check if visit the URL with the hash, the headline is active + it('should be active if the URL has the hash', () => { + /* Testing the active headline with Headline1 */ + + // Set the URL with the hash + mockRouter.asPath = '/#what-is-json-schema'; + + // Check if Correct headline is active + cy.mount(What is JSON Schema?); + cy.get('span').should( + 'have.class', + 'text-startBlue dark:text-endBlue inline-block ml-2', + ); + cy.get('span').should('have.text', 'ยถ'); + }); +}); diff --git a/cypress/components/JsonEditor.cy.tsx b/cypress/components/JsonEditor.cy.tsx new file mode 100644 index 000000000..b31e24396 --- /dev/null +++ b/cypress/components/JsonEditor.cy.tsx @@ -0,0 +1,216 @@ +import JsonEditor from '~/components/JsonEditor'; +import React from 'react'; +import mockNextRouter, { MockRouter } from '../plugins/mockNextRouterUtils'; + +/* + * Testing the JSON Editor Component involves testing some basic functionality, + * such as rendering, copying text to clipboard, navigating to the reference page, + * and schema compliance. Since the component uses a third-party library, + * testing its functionality can be a bit tricky. + * Note: Using istabul ignore in most of the small blocks because it is not possible to test them as * they are related to the third-party library. + */ + +// initial code for json editor +const initialCode = { + $schema: 'https://json-schema.org/draft/2020-12/schema', + $id: 'https://example.com/product.schema.json', + title: 'Product', + description: 'A product from dummy catalog', + type: 'object', + properties: { + productId: { + description: 'The unique identifier for a product', + type: 'integer', + }, + productName: { + description: 'Name of the product', + type: 'string', + }, + price: { + description: 'The price of the product', + type: 'number', + exclusiveMinimum: 0, + }, + tags: { + description: 'Tags for the product', + type: 'array', + items: { + type: 'string', + }, + minItems: 1, + uniqueItems: true, + }, + dimensions: { + type: 'object', + properties: { + length: { + type: 'number', + }, + width: { + type: 'number', + }, + height: { + type: 'number', + }, + }, + required: ['length', 'width', 'height'], + }, + // hypothetical fields + patternField: { + type: 'string', + pattern: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$', + }, + isPremium: { + type: 'boolean', + }, + nullField: { + type: 'null', + }, + }, + required: ['productId', 'productName', 'price'], +}; + +describe('JSON Editor Component', () => { + // eslint-disable-next-line + let mockRouter: MockRouter; + + beforeEach(() => { + // Create mock router object + cy.viewport(1200, 800); + mockRouter = mockNextRouter(); + }); + + // should render with default initial code prop + it('should render with default initial code prop', () => { + cy.mount(); + }); + + // should copies text to clipboard when clicked + it('should copy text to clipboard when clicked', () => { + // mock clipboard writeText + cy.window().then((win) => { + cy.stub(win.navigator.clipboard, 'writeText').as('clipboardWriteText'); + }); + + // mount component + cy.mount(); + + // check if copy img is visible + cy.get('[data-test="copy-clipboard-button"]') + .children('img') + .should('have.length', 2) + .first() + .should('have.attr', 'src', '/icons/copy.svg') + .should('be.visible'); + + // click on copy img + cy.get('[data-test="copy-clipboard-button"]').click(); + + // check if clipboard writeText is copied the correct code + cy.get('@clipboardWriteText').should( + 'have.been.calledWith', + JSON.stringify(initialCode, null, 2) + '\n', + ); + + // check if copied img is visible + cy.get('[data-test="copy-clipboard-button"]') + .children('img') + .last() + .should('have.attr', 'src', '/icons/copied.svg') + .should('be.visible'); + + // after 2 seconds, check if copy img is visible again + cy.get('[data-test="copy-clipboard-button"]') + .children('img') + .should('have.length', 2) + .first() + .should('have.attr', 'src', '/icons/copy.svg') + .should('be.visible'); + }); + + // should navigate correctly to each type-keyword reference page when clicked on type-keyword + it('should navigate correctly to each type-keyword reference page when clicked on type-keyword', () => { + cy.mount(); + + // type keywords to test + const type_keywords = [ + 'string', + 'object', + 'boolean', + 'null', + 'array', + 'number', + 'integer', + ]; + + // iterate over each type keyword and click on it and check if each keyword navigates to the correct page + type_keywords.forEach((type) => { + cy.get('span') + .contains(`"${type}"`, { matchCase: false }) + .then(($spans) => { + $spans.each((index, span) => { + // click on each type keyword + cy.wrap(span).click(); + + const link = `/understanding-json-schema/reference/${type == 'number' || type == 'integer' ? `numeric#${type}` : type}`; + + // check if router.push is called with correct url + cy.get('@routerPush').should('have.been.calledWith', link); + + // reset router.push + cy.get('@routerPush').invoke('reset'); + }); + }); + }); + }); + + // should render with meta regex props and check schema compliant + it('should render with meta regex props', () => { + const schemaCompliantMetaProps = + '// props { "valid": true, "caption": "valid - An Object is allowed." }\n{ "ok": "yes" }'; + + cy.mount(); + + // check if given object have schema field + cy.get('[data-test="check-json-schema"]').should('not.contain', 'schema'); + cy.get('[data-test="check-json-schema"]').contains('data'); + + // check if given object is schema compliant + cy.get('[data-test="compliant-to-schema"]').contains('compliant to schema'); + + // check if meta props caption is present + cy.get('[data-test="code-caption"]').contains( + 'valid - An Object is allowed.', + ); + + // Now test with invalid object and not schema compliant + const nonSchemaCompliantMetaProps = + '// props { "valid": false, "caption": "valid - An Object is allowed." }\n{ "123" }'; + cy.mount(); + + // check if given object is non schema compliant + cy.get('[data-test="not-compliant-to-schema"]').contains( + 'not compliant to schema', + ); + + // check when there is error in meta props + const errorInMetaProps = '// props { "valid" = false }\n{ "123" }'; + cy.mount(); + }); + + // mock copy function of editor when using keyboard shortcut + it('should copy selected text', () => { + const smallInitialCode = '{ const foo = "bar"; }'; + + // mock clipboard writeText + cy.window().then((win) => { + cy.stub(win.navigator.clipboard, 'writeText').as('clipboardWriteText'); + }); + + cy.mount(); + + // copy with keyboard shortcut || mouse selection + cy.get('[data-test="json-editor"]').trigger('copy'); + cy.get('@clipboardWriteText').should('have.been.calledWith', ''); + }); +}); diff --git a/cypress/components/Remember.cy.tsx b/cypress/components/Remember.cy.tsx new file mode 100644 index 000000000..970cdeda7 --- /dev/null +++ b/cypress/components/Remember.cy.tsx @@ -0,0 +1,63 @@ +import React from 'react'; +import { Remember } from '~/components/Remember'; + +describe('Remember Component', () => { + // Should render the Remember component correctly + it('should render the Remember component', () => { + // Mount the Remember component + cy.mount(); + + // Should have the correct elements and text + + cy.get('[data-test="remember-heading"]') + .should('have.prop', 'tagName', 'H3') + .and('contain.text', 'Remember'); + + cy.get('[data-test="contribute-docs-span"]') + .should('have.prop', 'tagName', 'SPAN') + .and('contain.text', 'Contribute to the JSON Schema Docs'); + + cy.get('[data-test="contribute-docs-div"]').should('exist'); + + cy.get('[data-test="get-started-span"]') + .should('have.prop', 'tagName', 'SPAN') + .and('contain.text', 'To get started as a Docs contributor:'); + + cy.get('ol').should('exist'); + + cy.get('li').should('have.length', 4); + + cy.get('[data-test="contribute-docs-questions-span"]') + .should('have.prop', 'tagName', 'SPAN') + .and('contain.text', 'Docs contributor questions?'); + + cy.get('[data-test="contribute-docs-questions-div"]').should('exist'); + + // Should have the correct links + cy.get('a').should('have.length', 4); + cy.get('a') + .eq(0) + .should( + 'have.attr', + 'href', + 'https://github.com/json-schema-org/community/blob/main/CONTRIBUTING.md', + ); + cy.get('a') + .eq(1) + .should( + 'have.attr', + 'href', + 'https://github.com/json-schema-org/.github/blob/main/CODE_OF_CONDUCT.md', + ); + cy.get('a') + .eq(2) + .should( + 'have.attr', + 'href', + 'https://github.com/orgs/json-schema-org/projects/16', + ); + cy.get('a') + .eq(3) + .should('have.attr', 'href', 'https://json-schema.org/slack'); + }); +}); diff --git a/cypress/components/ScrollButton.cy.tsx b/cypress/components/ScrollButton.cy.tsx new file mode 100644 index 000000000..44bd7a7cd --- /dev/null +++ b/cypress/components/ScrollButton.cy.tsx @@ -0,0 +1,36 @@ +import React from 'react'; +import ScrollButton from '~/components/ScrollButton'; + +describe('ScrollButton Component', () => { + // Should render and function correctly + it('should render and function correctly', () => { + // Mount the ScrollButton component + cy.mount( +
+ +
, + ); + + // Initially, the button should not exist + cy.get('[data-test="scroll-button"]').should('not.exist'); + + // when window scrollY is >150 the button should exist + cy.window().scrollTo(0, 151); + + // Check if the button is exist + cy.get('[data-test="scroll-button"]').should('exist'); + + // Click the button + cy.get('[data-test="scroll-button"]').click(); + + // Check if the window scroll to top + cy.window().its('scrollY').should('eq', 1); + + // check again if the button is not exist + cy.get('[data-test="scroll-button"]').should('not.exist'); + + // when window scrollY is <150 the button should not exist + cy.window().scrollTo(0, 149); + cy.get('[data-test="scroll-button"]').should('not.exist'); + }); +}); diff --git a/cypress/plugins/mockNextRouterUtils.ts b/cypress/plugins/mockNextRouterUtils.ts new file mode 100644 index 000000000..c14185e2a --- /dev/null +++ b/cypress/plugins/mockNextRouterUtils.ts @@ -0,0 +1,43 @@ +/** + * Mocks the Next.js router object. + * @returns The mock router object. + */ + +export interface MockRouter { + push: any; + replace: any; + prefetch: any; + pathname: string; + query: Record; + asPath: string; + events: { + on: any; + off: any; + emit: any; + }; +} + +export default function mockNextRouter() { + const push = cy.stub().as('routerPush'); + const replace = cy.stub().as('routerReplace'); + const prefetch = cy.stub().as('routerPrefetch'); + + const mockRouter: MockRouter = { + push, + replace, + prefetch, + pathname: '/', + query: {}, + asPath: '/', + events: { + on: cy.stub(), + off: cy.stub(), + emit: cy.stub(), + }, + }; + + // eslint-disable-next-line @typescript-eslint/no-var-requires + cy.stub(require('next/router'), 'useRouter').returns(mockRouter); + + return mockRouter; +} diff --git a/data/keywords.yml b/data/keywords.yml new file mode 100644 index 000000000..08df28e54 --- /dev/null +++ b/data/keywords.yml @@ -0,0 +1,460 @@ +- name: $anchor + vocabulary: [core] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/core/anchor' + links: + - url: 'https://json-schema.org/understanding-json-schema/structuring#anchor' + title: Structuring a complex schema ($anchor) + - url: 'https://json-schema.org/learn/json-schema-examples#ecommerce-system' + title: Ecommerce system example + - url: 'https://json-schema.org/draft/2020-12/json-schema-core#anchor' + title: Draft 2020-12 +- name: $comment + vocabulary: [core] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/core/comment' + links: + - url: 'https://json-schema.org/understanding-json-schema/reference/comments#comments' + title: Generic Keyowrds ($comment) + - url: 'https://json-schema.org/draft/2020-12/json-schema-core#name-comments-with-comment' + title: Draft 2020-12 +- name: $defs + vocabulary: [core] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/core/defs' + links: + - url: 'https://json-schema.org/learn/miscellaneous-examples' + title: Miscellaneous examples + - url: 'https://json-schema.org/understanding-json-schema/structuring#defs' + title: Structuring a complex schema ($defs) + - url: 'https://json-schema.org/draft/2020-12/json-schema-core#name-schema-re-use-with-defs' + title: Draft 2020-12 +- name: $dynamicAnchor + vocabulary: [core] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/core/dynamicanchor' + links: + - url: 'https://json-schema.org/draft/2020-12/json-schema-core#name-example-of-recursive-schema' + title: Draft 2020-12 +- name: $dynamicRef + vocabulary: [core] + learnjsonschemalink: 'https://json-schema.org/draft/2020-12/json-schema-core#dynamic-ref' + links: + - url: 'https://json-schema.org/draft/2020-12/json-schema-core#name-example-of-recursive-schema' + title: Draft 2020-12 +- name: $id + vocabulary: [core] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/core/id' + links: + - url: 'https://json-schema.org/learn/getting-started-step-by-step#create-a-schema-definition' + title: Create a schema defination + - url: 'https://json-schema.org/understanding-json-schema/basics#declaring-a-unique-identifier' + title: Declaring a unique identifer + - url: 'https://json-schema.org/draft/2020-12/json-schema-core#name-the-id-keyword' + title: Draft 2020-12 +- name: $ref + vocabulary: [core] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/core/ref' + links: + - url: 'https://json-schema.org/understanding-json-schema/basics#declaring-a-json-schema' + title: Declaring a JSON Schema + - url: 'https://json-schema.org/learn/getting-started-step-by-step#create-a-schema-definition' + title: Create a schema defination + - url: 'https://json-schema.org/understanding-json-schema/reference/array#unevaluateditems' + title: Unevaluated Items + - url: 'https://json-schema.org/understanding-json-schema/structuring#dollarref' + title: Structuring a complex schema ($ref) + - url: 'https://json-schema.org/draft/2020-12/json-schema-core#ref' + title: Draft 2020-12 +- name: $schema + vocabulary: [core] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/core/schema' + links: + - url: 'https://json-schema.org/understanding-json-schema/reference/schema' + title: Declaring a dialect ($schema) + - url: 'https://json-schema.org/draft/2020-12/json-schema-core#name-the-schema-keyword' + title: Draft 2020-12 +- name: $vocabulary + vocabulary: [core] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/core/vocabulary' + links: + - url: 'https://json-schema.org/draft/2020-12/json-schema-core#name-the-vocabulary-keyword' + title: Draft 2020-12 +- name: additionalProperties + vocabulary: [Applicator] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/applicator/additionalproperties' + links: + - url: 'https://json-schema.org/learn/file-system#modeling-a-file-system-with-json-schema' + title: Modeling a file system with JSON Schema + - url: 'https://json-schema.org/understanding-json-schema/reference/object#additionalproperties' + title: Additional Properties + - url: 'https://json-schema.org/draft/2020-12/json-schema-core#name-additionalproperties' + title: Draft 2020-12 +- name: allOf + vocabulary: [Applicator] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/applicator/allof' + links: + - url: 'https://json-schema.org/understanding-json-schema/reference/array#unevaluateditems' + title: Unevaluated Items + - url: 'https://json-schema.org/understanding-json-schema/reference/combining#schema-composition' + title: Schema Composition + - url: 'https://json-schema.org/draft/2020-12/json-schema-core#name-allof' + title: Draft 2020-12 +- name: anyOf + vocabulary: [Applicator] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/applicator/anyof' + links: + - url: 'https://json-schema.org/understanding-json-schema/reference/array#unevaluateditems' + title: Unevaluated Items + - url: 'https://json-schema.org/understanding-json-schema/reference/combining#schema-composition' + title: Schema Composition + - url: 'https://json-schema.org/draft/2020-12/json-schema-core#name-anyof' + title: Draft 2020-12 +- name: contains + vocabulary: [Applicator] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/applicator/contains' + links: + - url: 'https://json-schema.org/understanding-json-schema/reference/array#contains' + title: Contains + - url: 'https://json-schema.org/draft/2020-12/json-schema-core#name-contains' + title: Draft 2020-12 +- name: else + vocabulary: [Applicator] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/applicator/else' + links: + - url: 'https://json-schema.org/learn/miscellaneous-examples' + title: Miscellaneous Examples + - url: 'https://json-schema.org/understanding-json-schema/reference/conditionals#ifthenelse' + title: If-Then-Else + - url: 'https://json-schema.org/draft/2020-12/json-schema-core#name-else' + title: Draft 2020-12 +- name: if + vocabulary: [Applicator] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/applicator/if' + links: + - url: 'https://json-schema.org/learn/miscellaneous-examples' + title: Miscellaneous Examples + - url: 'https://json-schema.org/understanding-json-schema/reference/conditionals#ifthenelse' + title: If-Then-Else + - url: 'https://json-schema.org/draft/2020-12/json-schema-core#name-if' + title: Draft 2020-12 +- name: items + vocabulary: [Applicator] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/applicator/items' + links: + - url: 'https://json-schema.org/learn/file-system#modeling-a-file-system-with-json-schema' + title: Modeling a file system with JSON Schema + - url: 'https://json-schema.org/understanding-json-schema/reference/array#items' + title: Items + - url: 'https://json-schema.org/draft/2020-12/json-schema-core#name-items' + title: Draft 2020-12 +- name: not + vocabulary: [Applicator] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/applicator/not' + links: + - url: 'https://json-schema.org/understanding-json-schema/reference/combining#schema-composition' + title: Schema Compositon + - url: 'https://json-schema.org/draft/2020-12/json-schema-core#name-not' + title: Draft 2020-12 +- name: oneOf + vocabulary: [Applicator] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/applicator/oneof' + links: + - url: 'https://json-schema.org/learn/file-system#modeling-a-file-system-with-json-schema' + title: Modeling a file system with JSON Schema + - url: 'https://json-schema.org/understanding-json-schema/reference/array#unevaluateditems' + title: Unevaluted Items + - url: 'https://json-schema.org/understanding-json-schema/reference/combining#schema-composition' + title: Schema Compositon + - url: 'https://json-schema.org/draft/2020-12/json-schema-core#name-oneof' + title: Draft 2020-12 +- name: patternProperties + vocabulary: [Applicator] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/applicator/patternproperties' + links: + - url: 'https://json-schema.org/learn/file-system#modeling-a-file-system-with-json-schema' + title: Modeling a file system with JSON Schema + - url: 'https://json-schema.org/understanding-json-schema/reference/object#patternProperties' + title: Pattern Properties + - url: 'https://json-schema.org/draft/2020-12/json-schema-core#name-patternproperties' + title: Draft 2020-12 +- name: prefixitems + vocabulary: [Applicator] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/applicator/prefixitems' + links: + - url: 'https://json-schema.org/understanding-json-schema/reference/array#tupleValidation' + title: Tuple Validation + - url: 'https://json-schema.org/draft/2020-12/json-schema-core#name-prefixitems' + title: Draft 2020-12 +- name: properties + vocabulary: [Applicator] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/applicator/properties' + links: + - url: 'https://json-schema.org/understanding-json-schema/reference/object#properties' + title: Properties + - url: 'https://json-schema.org/learn/getting-started-step-by-step#create-a-schema-definition' + title: Schema Defination + - url: 'https://json-schema.org/draft/2020-12/json-schema-core#name-properties' + title: Draft 2020-12 +- name: propertynames + vocabulary: [Applicator] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/applicator/propertynames' + links: + - url: 'https://json-schema.org/understanding-json-schema/reference/object#propertyNames' + title: Property names + - url: 'https://json-schema.org/draft/2020-12/json-schema-core#name-propertynames' + title: Draft 2020-12 +- name: then + vocabulary: [Applicator] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/applicator/then' + links: + - url: 'https://json-schema.org/learn/miscellaneous-examples' + title: Miscellaneous Examples + - url: 'https://json-schema.org/understanding-json-schema/reference/conditionals#ifthenelse' + title: If-Then-Else + - url: 'https://json-schema.org/draft/2020-12/json-schema-core#name-then' + title: Draft 2020-12 +- name: const + vocabulary: [Validation] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/validation/const' + links: + - url: 'https://json-schema.org/learn/miscellaneous-examples' + title: Miscellaneous Examples + - url: 'https://json-schema.org/draft/2020-12/json-schema-validation#name-const' + title: Draft 2020-12 +- name: dependentRequired + vocabulary: [Validation] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/validation/dependentrequired' + links: + - url: 'https://json-schema.org/learn/miscellaneous-examples' + title: Miscellaneous Examples + - url: 'https://json-schema.org/understanding-json-schema/reference/conditionals#applying-subschemas-conditionally' + title: Applying Subschemas Conditionally + - url: 'https://json-schema.org/draft/2020-12/json-schema-validation#name-dependentrequired' + title: Draft 2020-12 +- name: dependentSchemas + vocabulary: [Validation] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/applicator/dependentschemas' + links: + - url: 'https://json-schema.org/understanding-json-schema/reference/conditionals#dependentSchemas' + title: Dependent Schemas + - url: 'https://json-schema.org/draft/2020-12/json-schema-core#name-dependentschemas' + title: Draft 2020-12 +- name: enum + vocabulary: [Validation] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/validation/enum' + links: + - url: 'https://json-schema.org/learn/miscellaneous-examples' + title: Miscellaneous Examples + - url: 'https://json-schema.org/understanding-json-schema/reference/enum#enumerated-values' + title: Enumerated Values + - url: 'https://json-schema.org/draft/2020-12/json-schema-validation#name-enum' + title: Draft 2020-12 +- name: exclusiveMaximum + vocabulary: [Validation] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/validation/exclusivemaximum' + links: + - url: 'https://json-schema.org/understanding-json-schema/reference/numeric#range' + title: Range + - url: 'https://json-schema.org/draft/2020-12/json-schema-validation#name-exclusivemaximum' + title: Draft 2020-12 +- name: exclusiveMinimum + vocabulary: [Validation] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/validation/exclusiveminimum' + links: + - url: 'https://json-schema.org/understanding-json-schema/reference/numeric#range' + title: Range + - url: 'https://json-schema.org/draft/2020-12/json-schema-validation#name-exclusiveminimum' + title: Draft 2020-12 +- name: maxContains + vocabulary: [Validation] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/validation/maxcontains' + links: + - url: 'https://json-schema.org/understanding-json-schema/reference/array#mincontains-maxcontains' + title: Mincontains / Maxcontains + - url: 'https://json-schema.org/draft/2020-12/json-schema-validation#name-maxcontains' + title: Draft 2020-12 +- name: maximum + vocabulary: [Validation] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/validation/maximum' + links: + - url: 'https://json-schema.org/learn/file-system#modeling-a-file-system-with-json-schema' + title: Modeling a file system with JSON Schema + - url: 'https://json-schema.org/understanding-json-schema/reference/numeric#range' + title: Range + - url: 'https://json-schema.org/draft/2020-12/json-schema-validation#name-maximum' + title: Draft 2020-12 +- name: minitems + vocabulary: [Validation] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/validation/minitems' + links: + - url: 'https://json-schema.org/learn/file-system#modeling-a-file-system-with-json-schema' + title: Modeling a file system with JSON Schema + - url: 'https://json-schema.org/understanding-json-schema/reference/array#length' + title: Length + - url: 'https://json-schema.org/draft/2020-12/json-schema-validation#name-minitems' + title: Draft 2020-12 +- name: minLength + vocabulary: [Validation] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/validation/minlength/' + links: + - url: 'https://json-schema.org/understanding-json-schema/reference/string#length' + title: Length + - url: 'https://json-schema.org/draft/2020-12/json-schema-validation#name-minlength' + title: Draft 2020-12 +- name: minProperties + vocabulary: [Validation] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/validation/minproperties' + links: + - url: 'https://json-schema.org/understanding-json-schema/reference/object#size' + title: Size + - url: 'https://json-schema.org/draft/2020-12/json-schema-validation#name-minproperties' + title: minProperties + - url: 'https://json-schema.org/draft/2020-12/json-schema-validation#name-minproperties' + title: Draft 2020-12 +- name: multipleOf + vocabulary: [Validation] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/validation/multipleof' + links: + - url: 'https://json-schema.org/understanding-json-schema/reference/numeric#multiples' + title: Multiples + - url: 'https://json-schema.org/draft/2020-12/json-schema-validation#name-multipleof' + title: Draft 2020-12 +- name: pattern + vocabulary: [Validation] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/validation/pattern' + links: + - url: 'https://json-schema.org/learn/miscellaneous-examples' + title: Miscellaneous Examples + - url: 'https://json-schema.org/understanding-json-schema/reference/string#regexp' + title: Regular Expressions + - url: 'https://json-schema.org/draft/2020-12/json-schema-validation#name-pattern' + title: Draft 2020-12 +- name: required + vocabulary: [Validation] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/validation/required' + links: + - url: 'https://json-schema.org/learn/file-system#modeling-a-file-system-with-json-schema' + title: Modeling a file system with JSON Schema + - url: 'https://json-schema.org/understanding-json-schema/reference/object#extending' + title: Extending Closed Schemas + - url: 'https://json-schema.org/draft/2020-12/json-schema-validation#name-required' + title: Draft 2020-12 +- name: type + vocabulary: [Validation] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/validation/type' + links: + - url: 'https://json-schema.org/understanding-json-schema/reference/type' + title: Type-specific keywords + - url: 'https://json-schema.org/understanding-json-schema/basics#the-type-keyword' + title: The type keyword + - url: 'https://json-schema.org/learn/getting-started-step-by-step#create-a-schema-definition' + title: Create a Schema Defination + - url: 'https://json-schema.org/draft/2020-12/json-schema-validation#name-type' + title: Draft 2020-12 +- name: uniqueItems + vocabulary: [Validation] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/validation/uniqueitems' + links: + - url: 'https://json-schema.org/learn/file-system#modeling-a-file-system-with-json-schema' + title: Modeling a file system with JSON Schema + - url: 'https://json-schema.org/understanding-json-schema/reference/array#uniqueItems' + title: Uniqueness + - url: 'https://json-schema.org/draft/2020-12/json-schema-validation#name-uniqueitems' + title: Draft 2020-12 +- name: contentEncoding + vocabulary: [Content] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/content/contentencoding' + links: + - url: 'https://json-schema.org/understanding-json-schema/reference/non_json_data#contentencoding' + title: contentEncoding + - url: 'https://json-schema.org/draft/2020-12/json-schema-validation#name-contentencoding' + title: Draft 2020-12 +- name: contentMediaType + vocabulary: [Content] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/content/contentmediatype' + links: + - url: 'https://json-schema.org/understanding-json-schema/reference/non_json_data#contentmediatype' + title: contentMediaType + - url: 'https://json-schema.org/draft/2020-12/json-schema-validation#name-contentmediatype' + title: Draft 2020-12 +- name: contentschema + vocabulary: [Content] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/content/contentschema' + links: + - url: 'https://json-schema.org/understanding-json-schema/reference/non_json_data#contentschema' + title: contentschema + - url: 'https://json-schema.org/draft/2020-12/json-schema-validation#name-contentschema' + title: Draft 2020-12 +- name: default + vocabulary: [Meta Data] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/meta-data/default' + links: + - url: 'https://json-schema.org/understanding-json-schema/reference/annotations' + title: Annotations + - url: 'https://json-schema.org/draft/2020-12/json-schema-validation#name-default' + title: Draft 2020-12 +- name: deprecated + vocabulary: [Meta Data] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/meta-data/deprecated' + links: + - url: 'https://json-schema.org/understanding-json-schema/reference/annotations' + title: Annotations + - url: 'https://json-schema.org/draft/2020-12/json-schema-validation#name-deprecated' + title: Draft 2020-12 +- name: description + vocabulary: [Meta Data] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/meta-data/description' + links: + - url: 'https://json-schema.org/understanding-json-schema/reference/annotations' + title: Annotations + - url: 'https://json-schema.org/learn/getting-started-step-by-step#create-a-schema-definition' + title: Create a schema definition + - url: 'https://json-schema.org/draft/2020-12/json-schema-validation#name-title-and-description' + title: Draft 2020-12 +- name: examples + vocabulary: [Meta Data] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/meta-data/examples' + links: + - url: 'https://json-schema.org/understanding-json-schema/reference/annotations' + title: Annotations + - url: 'https://json-schema.org/draft/2020-12/json-schema-validation#name-examples' + title: Draft 2020-12 +- name: readOnly + vocabulary: [Meta Data] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/meta-data/readonly' + links: + - url: 'https://json-schema.org/understanding-json-schema/reference/annotations' + title: Annotations + - url: 'https://json-schema.org/draft/2020-12/json-schema-validation#name-readonly-and-writeonly' + title: Draft 2020-12 +- name: title + vocabulary: [Meta Data] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/meta-data/title' + links: + - url: 'https://json-schema.org/understanding-json-schema/reference/annotations' + title: Annotations + - url: 'https://json-schema.org/learn/getting-started-step-by-step#create-a-schema-definition' + title: Create a schema definition + - url: 'https://json-schema.org/draft/2020-12/json-schema-validation#name-title-and-description' + title: Draft 2020-12 +- name: writeOnly + vocabulary: [Meta Data] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/meta-data/writeonly' + links: + - url: 'https://json-schema.org/understanding-json-schema/reference/annotations' + title: Annotations + - url: 'https://json-schema.org/draft/2020-12/json-schema-validation#name-readonly-and-writeonly' + title: Draft 2020-12 +- name: unevaluateditems + vocabulary: [Unevaluated] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/unevaluated/unevaluateditems' + links: + - url: 'https://json-schema.org/understanding-json-schema/reference/array#unevaluateditems' + title: Unevaluated Items + - url: 'https://json-schema.org/draft/2020-12/json-schema-core#name-unevaluateditems' + title: Draft 2020-12 +- name: unevaluatedproperties + vocabulary: [Unevaluated] + learnjsonschemalink: 'https://www.learnjsonschema.com/2020-12/unevaluated/unevaluatedproperties' + links: + - url: 'https://json-schema.org/understanding-json-schema/reference/object#unevaluatedproperties' + title: Unevaluated Properties + - url: 'https://json-schema.org/draft/2020-12/json-schema-core#name-unevaluatedproperties' + title: Draft 2020-12 diff --git a/data/validator-libraries-modern.yml b/data/validator-libraries-modern.yml index c50c8dd3f..5bbe03150 100644 --- a/data/validator-libraries-modern.yml +++ b/data/validator-libraries-modern.yml @@ -230,10 +230,9 @@ last-updated: "2022-08-31" - name: "@exodus/schemasafe" url: https://github.com/ExodusMovement/schemasafe - notes: | - For Node.js and browsers, with security and speed being the main focus. Pre-compiles schemas to JS functions. + notes: "For Node.js and browsers, with security and speed being the main focus. Pre-compiles schemas to JS functions. [Supports OpenAPI `discriminator`](https://github.com/ExodusMovement/schemasafe/blob/master/doc/Discriminator-support.md). - Default behavior is hardened with additional schema coherence validation. + Default behavior is hardened with additional schema coherence validation." date-draft: [2020-12, 2019-09] draft: [7, 6, 4] license: MIT @@ -568,6 +567,15 @@ notes: Supports formatting, linting, bundling, testing, validating, and more. As an exception, validation is not supported on Draft 3 and older built-on: name: jsontoolkit + - name: v8r + license: MIT + url: https://github.com/chris48s/v8r + last-updated: "2024-08-23" + date-draft: [2020-12, 2019-09] + draft: [7, 6, 4] + notes: Uses [Schema Store](https://www.schemastore.org/) to detect a suitable schema for your input files based on the filename + built-on: + name: ajv - name: Github Actions implementations: - name: Validate JSON Action diff --git a/package.json b/package.json index a21180287..752869d69 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "format:check": "prettier --check \"**/*.{ts,tsx,scss,css}\"", "prepare": "husky", "cypress:open": "cypress open", - "cypress:run": "cypress run", + "cypress:run:all": "cypress run && cypress run --component", "test:coverage:e2e": "cypress run --env coverage=true", "test:coverage:component": "cypress run --component --env coverage=true", "test:coverage:all": "yarn test:coverage:e2e && yarn test:coverage:component && nyc merge .nyc_output coverage.json && nyc report --reporter=html --reporter=text" @@ -28,7 +28,7 @@ "dependencies": { "@docsearch/react": "3.6.1", "@types/jsonpath": "^0.2.4", - "axios": "1.7.2", + "axios": "1.7.4", "babel-loader": "^9.1.3", "classnames": "^2.3.1", "feed": "^4.2.2", @@ -43,14 +43,14 @@ "next": "14.2.5", "next-sitemap": "^4.2.3", "next-themes": "^0.3.0", - "node-ical": "0.16.1", + "node-ical": "0.19.0", "react": "18.3.1", "react-dom": "18.3.1", "react-syntax-highlighter": "^15.5.0", "react-text-truncate": "^0.19.0", "reading-time": "^1.5.0", "slate": "^0.100.0", - "slate-react": "^0.107.1", + "slate-react": "^0.108.0", "slugify": "^1.6.5", "yarn": "1.22.19", "zero-fill": "^2.2.4", @@ -59,20 +59,20 @@ "devDependencies": { "@babel/core": "^7.25.2", "@babel/preset-env": "^7.25.3", - "@cypress/code-coverage": "^3.12.44", + "@cypress/code-coverage": "^3.12.46", "@next/eslint-plugin-next": "^14.0.1", "@svgr/webpack": "^8.1.0", "@types/babel__core": "^7", "@types/babel__preset-env": "^7", "@types/file-saver": "^2.0.7", "@types/js-yaml": "^4.0.5", - "@types/node": "^20.14.10", - "@types/react": "18.3.3", + "@types/node": "^22.4.2", + "@types/react": "18.3.5", "@types/react-syntax-highlighter": "^15.5.13", "@types/react-text-truncate": "^0.14.1", "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.21.0", - "autoprefixer": "^10.4.16", + "autoprefixer": "^10.4.20", "babel-plugin-istanbul": "^7.0.0", "cypress": "^13.13.1", "eslint": "8.57.0", diff --git a/pages/implementations/main.md b/pages/implementations/main.md index 71d3f0dd2..142aed4b5 100644 --- a/pages/implementations/main.md +++ b/pages/implementations/main.md @@ -77,6 +77,8 @@ For example, the only incompatibilities between draft-04 and draft-06 involve `e - [jsonschema.net](https://www.jsonschema.net/) - generates schemas from example data - [Liquid Online Tools](https://www.liquid-technologies.com/online-json-to-schema-converter) - infer JSON Schema from sample JSON data - [quicktype.io](https://app.quicktype.io/#l=schema) - infer JSON Schema from samples, and generate TypeScript, C++, go, Java, C#, Swift, etc. types from JSON Schema +- Helm + - [dadav/helm-schema](https://github.com/dadav/helm-schema) (MIT) - generate _values.schema.json_ from Helm _values.yaml_; compatible with [helm-docs](https://github.com/norwoodj/helm-docs) comments ### From model diff --git a/pages/understanding-json-schema/keywords/index.page.tsx b/pages/understanding-json-schema/keywords/index.page.tsx new file mode 100644 index 000000000..ffa2e72f0 --- /dev/null +++ b/pages/understanding-json-schema/keywords/index.page.tsx @@ -0,0 +1,94 @@ +import React from 'react'; +import Head from 'next/head'; + +import fs from 'fs'; +import { getLayout } from '~/components/Sidebar'; +import yaml from 'js-yaml'; +import { SectionContext } from '~/context'; +import { Headline1, Headline4 } from '~/components/Headlines'; +import { DocsHelp } from '~/components/DocsHelp'; +import Link from 'next/link'; +import Image from 'next/image'; + +export async function getStaticProps() { + const datas = yaml.load( + fs.readFileSync('data/keywords.yml', 'utf-8'), + ) as DataObject[]; + + // Sort the data alphabetically by the 'name' keyword + datas.sort((a, b) => a.name.localeCompare(b.name)); + + return { + props: { + datas, + }, + }; +} + +interface DataObject { + name: string; + vocabulary: string[]; + learnjsonschemalink: string; + links?: LinkObject[]; +} + +interface LinkObject { + url: string; + title: string; +} + +export default function StaticMarkdownPage({ datas }: { datas: DataObject[] }) { + const markdownFile = '_index'; + return ( + + + JSON Schema - Keywords + + JSON Schema Keywords +

+ JSON Schema keywords are the building blocks of JSON Schema and they are + used to define the structure of a JSON document. +

+

+ Find below the list of JSON Schema Keywords and their links to the JSON + Schema docs: +

+
+ {datas + .sort((a: DataObject, b: DataObject) => a.name.localeCompare(b.name)) + .map( + (data: DataObject, index: number) => + data.links && ( +
+
+ {data.name} + + external link + +
+
    + {data.links?.map((link: LinkObject, index: number) => ( +
  • + + {link.title} + +
  • + ))} +
+
+ ), + )} +
+ + +
+ ); +} +StaticMarkdownPage.getLayout = getLayout; diff --git a/project_data.json b/project_data.json new file mode 100644 index 000000000..9b7a787de --- /dev/null +++ b/project_data.json @@ -0,0 +1,573 @@ +[ + { + "id": "PVTI_lADOAMaoXc4ARJVmzgHJWGE", + "fieldValues": { + "nodes": [ + {}, + {}, + {}, + { + "text": "Strategy for engaging implementers", + "field": { + "name": "Title" + } + }, + { + "name": "Ecosystem", + "field": { + "name": "Category" + } + }, + { + "name": "Medium", + "field": { + "name": "Effort" + } + }, + { + "name": "High", + "field": { + "name": "Impact" + } + }, + { + "name": "In Progress", + "field": { + "name": "Status" + } + } + ] + }, + "content": { + "title": "Strategy for engaging implementers", + "assignees": { + "nodes": [ + { + "login": "Relequestual" + }, + { + "login": "benjagm" + } + ] + } + } + }, + { + "id": "PVTI_lADOAMaoXc4ARJVmzgHJWH0", + "fieldValues": { + "nodes": [ + {}, + {}, + {}, + { + "text": "Complete the new version of the Spec", + "field": { + "name": "Title" + } + }, + { + "name": "Specification", + "field": { + "name": "Category" + } + }, + { + "name": "High", + "field": { + "name": "Effort" + } + }, + { + "name": "High", + "field": { + "name": "Impact" + } + }, + { + "name": "In Progress", + "field": { + "name": "Status" + } + } + ] + }, + "content": { + "title": "Complete the new version of the Spec", + "assignees": { + "nodes": [ + { + "login": "jdesrosiers" + }, + { + "login": "Relequestual" + }, + { + "login": "gregsdennis" + }, + { + "login": "benjagm" + } + ] + } + } + }, + { + "id": "PVTI_lADOAMaoXc4ARJVmzgHWFHc", + "fieldValues": { + "nodes": [ + {}, + {}, + {}, + { + "text": "Complete OpenJSF onboard", + "field": { + "name": "Title" + } + }, + { + "name": "Medium", + "field": { + "name": "Effort" + } + }, + { + "name": "Medium-high", + "field": { + "name": "Impact" + } + }, + { + "name": "Paused", + "field": { + "name": "Status" + } + }, + { + "name": "Operations", + "field": { + "name": "Category" + } + } + ] + }, + "content": { + "title": "Complete OpenJSF onboard", + "assignees": { + "nodes": [ + { + "login": "Relequestual" + } + ] + } + } + }, + { + "id": "PVTI_lADOAMaoXc4ARJVmzgHJWPU", + "fieldValues": { + "nodes": [ + {}, + {}, + {}, + { + "text": "Research of existing implementations by language", + "field": { + "name": "Title" + } + }, + { + "name": "Ecosystem", + "field": { + "name": "Category" + } + }, + { + "name": "Low", + "field": { + "name": "Effort" + } + }, + { + "name": "High", + "field": { + "name": "Impact" + } + }, + { + "name": "In Progress", + "field": { + "name": "Status" + } + } + ] + }, + "content": { + "title": "Research of existing implementations by language", + "assignees": { + "nodes": [ + { + "login": "Julian" + }, + { + "login": "benjagm" + } + ] + } + } + }, + { + "id": "PVTI_lADOAMaoXc4ARJVmzgHJWSI", + "fieldValues": { + "nodes": [ + {}, + {}, + {}, + { + "text": "Report on projects financial sustainability requirements", + "field": { + "name": "Title" + } + }, + { + "name": "Operations", + "field": { + "name": "Category" + } + }, + { + "name": "Low-medium", + "field": { + "name": "Effort" + } + }, + { + "name": "Low-medium", + "field": { + "name": "Impact" + } + }, + { + "name": "Planned", + "field": { + "name": "Status" + } + } + ] + }, + "content": { + "title": "Report on projects financial sustainability requirements", + "assignees": { + "nodes": [ + { + "login": "Julian" + } + ] + } + } + }, + { + "id": "PVTI_lADOAMaoXc4ARJVmzgHJZP0", + "fieldValues": { + "nodes": [ + {}, + {}, + {}, + { + "text": "Implement and scale the new governance model", + "field": { + "name": "Title" + } + }, + { + "name": "Low", + "field": { + "name": "Effort" + } + }, + { + "name": "Medium", + "field": { + "name": "Impact" + } + }, + { + "name": "In Progress", + "field": { + "name": "Status" + } + }, + { + "name": "Operations", + "field": { + "name": "Category" + } + } + ] + }, + "content": { + "title": "Implement and scale the new governance model", + "assignees": { + "nodes": [ + { + "login": "Relequestual" + } + ] + } + } + }, + { + "id": "PVTI_lADOAMaoXc4ARJVmzgHJZUk", + "fieldValues": { + "nodes": [ + {}, + {}, + {}, + { + "text": "Enhance public communications strategy", + "field": { + "name": "Title" + } + }, + { + "name": "Low", + "field": { + "name": "Effort" + } + }, + { + "name": "Medium", + "field": { + "name": "Impact" + } + }, + { + "name": "Deferred", + "field": { + "name": "Status" + } + }, + { + "name": "Communications", + "field": { + "name": "Category" + } + } + ] + }, + "content": { + "title": "Enhance public communications strategy", + "assignees": { + "nodes": [ + { + "login": "Relequestual" + }, + { + "login": "benjagm" + } + ] + } + } + }, + { + "id": "PVTI_lADOAMaoXc4ARJVmzgHJV10", + "fieldValues": { + "nodes": [ + {}, + {}, + {}, + { + "text": "Identify a list of critical interfaces that implementations should have and empower implementers with better resources", + "field": { + "name": "Title" + } + }, + { + "name": "Ecosystem", + "field": { + "name": "Category" + } + }, + { + "name": "Low", + "field": { + "name": "Effort" + } + }, + { + "name": "High", + "field": { + "name": "Impact" + } + }, + { + "name": "Done", + "field": { + "name": "Status" + } + } + ] + }, + "content": { + "title": "Identify a list of critical interfaces that implementations should have and empower implementers with better resources", + "assignees": { + "nodes": [ + { + "login": "Julian" + }, + { + "login": "gregsdennis" + } + ] + } + } + }, + { + "id": "PVTI_lADOAMaoXc4ARJVmzgHdNFs", + "fieldValues": { + "nodes": [ + {}, + {}, + {}, + { + "text": "Complete and publish the new website", + "field": { + "name": "Title" + } + }, + { + "name": "Low", + "field": { + "name": "Effort" + } + }, + { + "name": "High", + "field": { + "name": "Impact" + } + }, + { + "name": "Done", + "field": { + "name": "Status" + } + }, + { + "name": "User Experience", + "field": { + "name": "Category" + } + } + ] + }, + "content": { + "title": "Complete and publish the new website", + "assignees": { + "nodes": [ + { + "login": "benjagm" + } + ] + } + } + }, + { + "id": "PVTI_lADOAMaoXc4ARJVmzgK4C9o", + "fieldValues": { + "nodes": [ + {}, + {}, + {}, + { + "text": "Define the JSON Schema Documentation Strategy", + "field": { + "name": "Title" + } + }, + { + "name": "High", + "field": { + "name": "Effort" + } + }, + { + "name": "Medium", + "field": { + "name": "Impact" + } + }, + { + "name": "Done", + "field": { + "name": "Status" + } + }, + { + "name": "User Experience", + "field": { + "name": "Category" + } + } + ] + }, + "content": { + "title": "Define the JSON Schema Documentation Strategy", + "assignees": { + "nodes": [ + { + "login": "benjagm" + } + ] + } + } + }, + { + "id": "PVTI_lADOAMaoXc4ARJVmzgHJWDE", + "fieldValues": { + "nodes": [ + {}, + {}, + {}, + { + "text": "Refine the contributor journey", + "field": { + "name": "Title" + } + }, + { + "name": "User Experience", + "field": { + "name": "Category" + } + }, + { + "name": "High", + "field": { + "name": "Effort" + } + }, + { + "name": "Medium-high", + "field": { + "name": "Impact" + } + }, + { + "name": "Done", + "field": { + "name": "Status" + } + } + ] + }, + "content": { + "title": "Refine the contributor journey", + "assignees": { + "nodes": [ + { + "login": "benjagm" + } + ] + } + } + } +] diff --git a/public/icons/external-link.svg b/public/icons/external-link.svg new file mode 100644 index 000000000..534341dde --- /dev/null +++ b/public/icons/external-link.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index ffed510cf..dee887347 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1752,14 +1752,14 @@ __metadata: languageName: node linkType: hard -"@cypress/code-coverage@npm:^3.12.44": - version: 3.12.44 - resolution: "@cypress/code-coverage@npm:3.12.44" +"@cypress/code-coverage@npm:^3.12.46": + version: 3.12.46 + resolution: "@cypress/code-coverage@npm:3.12.46" dependencies: "@cypress/webpack-preprocessor": "npm:^6.0.0" chalk: "npm:4.1.2" dayjs: "npm:1.11.12" - debug: "npm:4.3.5" + debug: "npm:4.3.6" execa: "npm:4.1.0" globby: "npm:11.1.0" istanbul-lib-coverage: "npm:^3.0.0" @@ -1771,7 +1771,7 @@ __metadata: babel-loader: ^8.3 || ^9 cypress: "*" webpack: ^4 || ^5 - checksum: 10c0/6f6915c7d1840ac306f3344ce27d9391f7629f7a48b5ad305d14050cc67a6e9ac4f533a7b7cfda50717b36eef3970da48ed62ecb162169ddeb9fd8483d7c1c0a + checksum: 10c0/23f02d8dfe44da0f95a0e43da2daa72db4f85f880fa98ccc078a3b2b26c5b9a0d1f5873b4eb26dffe5c6d230450eed4a0240fff4acfe9e6537bf38d0473e5f56 languageName: node linkType: hard @@ -2475,13 +2475,6 @@ __metadata: languageName: node linkType: hard -"@types/is-hotkey@npm:^0.1.8": - version: 0.1.10 - resolution: "@types/is-hotkey@npm:0.1.10" - checksum: 10c0/8ce7cc70a42e4191ba9871905a68fa6586a9da0bfa9940e20fe67e573b34bc79bfc23158e842c7bd3288ebf9c82ee5cce0c7fa1fe8245f6845c2f9b9f3afc536 - languageName: node - linkType: hard - "@types/js-yaml@npm:^4.0.5": version: 4.0.9 resolution: "@types/js-yaml@npm:4.0.9" @@ -2526,12 +2519,12 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^20.14.10": - version: 20.14.10 - resolution: "@types/node@npm:20.14.10" +"@types/node@npm:^22.4.2": + version: 22.4.2 + resolution: "@types/node@npm:22.4.2" dependencies: - undici-types: "npm:~5.26.4" - checksum: 10c0/0b06cff14365c2d0085dc16cc8cbea5c40ec09cfc1fea966be9eeecf35562760bfde8f88e86de6edfaf394501236e229d9c1084fad04fb4dec472ae245d8ae69 + undici-types: "npm:~6.19.2" + checksum: 10c0/db583e83230eed29eb73e24cc03637f5bdc8c01883ae0ba0a6ab9356065f93d0355a91ad1c89b985631006bd77c529d6a3ce042b3667b69c8851d05e1f965db1 languageName: node linkType: hard @@ -2560,7 +2553,7 @@ __metadata: languageName: node linkType: hard -"@types/react@npm:*, @types/react@npm:18.3.3": +"@types/react@npm:*": version: 18.3.3 resolution: "@types/react@npm:18.3.3" dependencies: @@ -2570,6 +2563,16 @@ __metadata: languageName: node linkType: hard +"@types/react@npm:18.3.5": + version: 18.3.5 + resolution: "@types/react@npm:18.3.5" + dependencies: + "@types/prop-types": "npm:*" + csstype: "npm:^3.0.2" + checksum: 10c0/548b1d3d7c2f0242fbfdbbd658731b4ce69a134be072fa83e6ab516f2840402a3f20e3e7f72e95133b23d4880ef24a6d864050dc8e1f7c68f39fa87ca8445917 + languageName: node + linkType: hard + "@types/semver@npm:^7.5.0": version: 7.5.8 resolution: "@types/semver@npm:7.5.8" @@ -3413,21 +3416,21 @@ __metadata: languageName: node linkType: hard -"autoprefixer@npm:^10.4.16": - version: 10.4.16 - resolution: "autoprefixer@npm:10.4.16" +"autoprefixer@npm:^10.4.20": + version: 10.4.20 + resolution: "autoprefixer@npm:10.4.20" dependencies: - browserslist: "npm:^4.21.10" - caniuse-lite: "npm:^1.0.30001538" - fraction.js: "npm:^4.3.6" + browserslist: "npm:^4.23.3" + caniuse-lite: "npm:^1.0.30001646" + fraction.js: "npm:^4.3.7" normalize-range: "npm:^0.1.2" - picocolors: "npm:^1.0.0" + picocolors: "npm:^1.0.1" postcss-value-parser: "npm:^4.2.0" peerDependencies: postcss: ^8.1.0 bin: autoprefixer: bin/autoprefixer - checksum: 10c0/e00256e754d481a026d928bca729b25954074dd142dbec022f0a7db0d3bbc0dc2e2dc7542e94fec22eff81e21fe140e6856448e2d9a002660cb1e2ad434daee0 + checksum: 10c0/e1f00978a26e7c5b54ab12036d8c13833fad7222828fc90914771b1263f51b28c7ddb5803049de4e77696cbd02bb25cfc3634e80533025bb26c26aacdf938940 languageName: node linkType: hard @@ -3468,25 +3471,25 @@ __metadata: languageName: node linkType: hard -"axios@npm:1.4.0": - version: 1.4.0 - resolution: "axios@npm:1.4.0" +"axios@npm:1.7.4": + version: 1.7.4 + resolution: "axios@npm:1.7.4" dependencies: - follow-redirects: "npm:^1.15.0" + follow-redirects: "npm:^1.15.6" form-data: "npm:^4.0.0" proxy-from-env: "npm:^1.1.0" - checksum: 10c0/a925a07590b0ec1d4daf28cd27890f930daab980371558deb3b883af174b881da09e5ba2cb8393a648fda5859e39934982d0b8b092fe89fc84cb6c80a70a1910 + checksum: 10c0/5ea1a93140ca1d49db25ef8e1bd8cfc59da6f9220159a944168860ad15a2743ea21c5df2967795acb15cbe81362f5b157fdebbea39d53117ca27658bab9f7f17 languageName: node linkType: hard -"axios@npm:1.7.2": - version: 1.7.2 - resolution: "axios@npm:1.7.2" +"axios@npm:1.7.7": + version: 1.7.7 + resolution: "axios@npm:1.7.7" dependencies: follow-redirects: "npm:^1.15.6" form-data: "npm:^4.0.0" proxy-from-env: "npm:^1.1.0" - checksum: 10c0/cbd47ce380fe045313364e740bb03b936420b8b5558c7ea36a4563db1258c658f05e40feb5ddd41f6633fdd96d37ac2a76f884dad599c5b0224b4c451b3fa7ae + checksum: 10c0/4499efc89e86b0b49ffddc018798de05fab26e3bf57913818266be73279a6418c3ce8f9e934c7d2d707ab8c095e837fc6c90608fb7715b94d357720b5f568af7 languageName: node linkType: hard @@ -3639,11 +3642,11 @@ __metadata: linkType: hard "braces@npm:^3.0.2, braces@npm:~3.0.2": - version: 3.0.2 - resolution: "braces@npm:3.0.2" + version: 3.0.3 + resolution: "braces@npm:3.0.3" dependencies: - fill-range: "npm:^7.0.1" - checksum: 10c0/321b4d675791479293264019156ca322163f02dc06e3c4cab33bb15cd43d80b51efef69b0930cfde3acd63d126ebca24cd0544fa6f261e093a0fb41ab9dda381 + fill-range: "npm:^7.1.1" + checksum: 10c0/7c6dfd30c338d2997ba77500539227b9d1f85e388a5f43220865201e407e076783d0881f2d297b9f80951b4c957fcf0b51c1d2d24227631643c3f7c284b0aa04 languageName: node linkType: hard @@ -3661,7 +3664,7 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.23.0": +"browserslist@npm:^4.23.0, browserslist@npm:^4.23.3": version: 4.23.3 resolution: "browserslist@npm:4.23.3" dependencies: @@ -4366,7 +4369,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4": +"debug@npm:4, debug@npm:4.3.6": version: 4.3.6 resolution: "debug@npm:4.3.6" dependencies: @@ -4378,7 +4381,16 @@ __metadata: languageName: node linkType: hard -"debug@npm:4.3.5, debug@npm:^4.1.0, debug@npm:^4.3.1": +"debug@npm:^3.1.0, debug@npm:^3.2.7": + version: 3.2.7 + resolution: "debug@npm:3.2.7" + dependencies: + ms: "npm:^2.1.1" + checksum: 10c0/37d96ae42cbc71c14844d2ae3ba55adf462ec89fd3a999459dec3833944cd999af6007ff29c780f1c61153bcaaf2c842d1e4ce1ec621e4fc4923244942e4a02a + languageName: node + linkType: hard + +"debug@npm:^4.1.0, debug@npm:^4.3.1": version: 4.3.5 resolution: "debug@npm:4.3.5" dependencies: @@ -4390,15 +4402,6 @@ __metadata: languageName: node linkType: hard -"debug@npm:^3.1.0, debug@npm:^3.2.7": - version: 3.2.7 - resolution: "debug@npm:3.2.7" - dependencies: - ms: "npm:^2.1.1" - checksum: 10c0/37d96ae42cbc71c14844d2ae3ba55adf462ec89fd3a999459dec3833944cd999af6007ff29c780f1c61153bcaaf2c842d1e4ce1ec621e4fc4923244942e4a02a - languageName: node - linkType: hard - "debug@npm:^4.1.1, debug@npm:^4.3.2, debug@npm:^4.3.4": version: 4.3.4 resolution: "debug@npm:4.3.4" @@ -5621,12 +5624,12 @@ __metadata: languageName: node linkType: hard -"fill-range@npm:^7.0.1": - version: 7.0.1 - resolution: "fill-range@npm:7.0.1" +"fill-range@npm:^7.1.1": + version: 7.1.1 + resolution: "fill-range@npm:7.1.1" dependencies: to-regex-range: "npm:^5.0.1" - checksum: 10c0/7cdad7d426ffbaadf45aeb5d15ec675bbd77f7597ad5399e3d2766987ed20bda24d5fac64b3ee79d93276f5865608bb22344a26b9b1ae6c4d00bd94bf611623f + checksum: 10c0/b75b691bbe065472f38824f694c2f7449d7f5004aa950426a2c28f0306c60db9b880c0b0e4ed819997ffb882d1da02cfcfc819bddc94d71627f5269682edf018 languageName: node linkType: hard @@ -5699,7 +5702,7 @@ __metadata: languageName: node linkType: hard -"follow-redirects@npm:^1.15.0, follow-redirects@npm:^1.15.6": +"follow-redirects@npm:^1.15.6": version: 1.15.6 resolution: "follow-redirects@npm:1.15.6" peerDependenciesMeta: @@ -5774,7 +5777,7 @@ __metadata: languageName: node linkType: hard -"fraction.js@npm:^4.3.6": +"fraction.js@npm:^4.3.7": version: 4.3.7 resolution: "fraction.js@npm:4.3.7" checksum: 10c0/df291391beea9ab4c263487ffd9d17fed162dbb736982dee1379b2a8cc94e4e24e46ed508c6d278aded9080ba51872f1bc5f3a5fd8d7c74e5f105b508ac28711 @@ -7131,7 +7134,7 @@ __metadata: dependencies: "@babel/core": "npm:^7.25.2" "@babel/preset-env": "npm:^7.25.3" - "@cypress/code-coverage": "npm:^3.12.44" + "@cypress/code-coverage": "npm:^3.12.46" "@docsearch/react": "npm:3.6.1" "@next/eslint-plugin-next": "npm:^14.0.1" "@svgr/webpack": "npm:^8.1.0" @@ -7140,14 +7143,14 @@ __metadata: "@types/file-saver": "npm:^2.0.7" "@types/js-yaml": "npm:^4.0.5" "@types/jsonpath": "npm:^0.2.4" - "@types/node": "npm:^20.14.10" - "@types/react": "npm:18.3.3" + "@types/node": "npm:^22.4.2" + "@types/react": "npm:18.3.5" "@types/react-syntax-highlighter": "npm:^15.5.13" "@types/react-text-truncate": "npm:^0.14.1" "@typescript-eslint/eslint-plugin": "npm:^6.21.0" "@typescript-eslint/parser": "npm:^6.21.0" - autoprefixer: "npm:^10.4.16" - axios: "npm:1.7.2" + autoprefixer: "npm:^10.4.20" + axios: "npm:1.7.4" babel-loader: "npm:^9.1.3" babel-plugin-istanbul: "npm:^7.0.0" classnames: "npm:^2.3.1" @@ -7177,7 +7180,7 @@ __metadata: next: "npm:14.2.5" next-sitemap: "npm:^4.2.3" next-themes: "npm:^0.3.0" - node-ical: "npm:0.16.1" + node-ical: "npm:0.19.0" nyc: "npm:^17.0.0" postcss: "npm:^8.4.41" prettier: "npm:3.3.3" @@ -7187,7 +7190,7 @@ __metadata: react-text-truncate: "npm:^0.19.0" reading-time: "npm:^1.5.0" slate: "npm:^0.100.0" - slate-react: "npm:^0.107.1" + slate-react: "npm:^0.108.0" slugify: "npm:^1.6.5" tailwindcss: "npm:^3.3.5" typescript: "npm:5.2.2" @@ -7555,13 +7558,6 @@ __metadata: languageName: node linkType: hard -"luxon@npm:^1.21.3": - version: 1.28.1 - resolution: "luxon@npm:1.28.1" - checksum: 10c0/5c561ce4364bb2301ca5811c74d11a9e087f82164109c7997dc8f0959e64d51259d8e630914dca2edc6702525ce5ab066a4b85caa19d04be71f10e79ffe2bc84 - languageName: node - linkType: hard - "make-dir@npm:^3.0.0, make-dir@npm:^3.0.2": version: 3.1.0 resolution: "make-dir@npm:3.1.0" @@ -7806,12 +7802,12 @@ __metadata: languageName: node linkType: hard -"moment-timezone@npm:^0.5.31": - version: 0.5.43 - resolution: "moment-timezone@npm:0.5.43" +"moment-timezone@npm:^0.5.45": + version: 0.5.45 + resolution: "moment-timezone@npm:0.5.45" dependencies: moment: "npm:^2.29.4" - checksum: 10c0/6f42174e01398d135883fb45aa820e57f34b25aab5ea6c50998139d06f975ee4457275b5e331466b14bff4d830e681c267f9cc462f445eb9c2b84d04add829f6 + checksum: 10c0/7497f23c4b8c875dbf07c03f9a1253f79edaeedc29d5732e36bfd3c5577e25aed1924fbd84cbb713ce1920dbe822be0e21bd487851a7d13907226f289a5e568b languageName: node linkType: hard @@ -7992,15 +7988,15 @@ __metadata: languageName: node linkType: hard -"node-ical@npm:0.16.1": - version: 0.16.1 - resolution: "node-ical@npm:0.16.1" +"node-ical@npm:0.19.0": + version: 0.19.0 + resolution: "node-ical@npm:0.19.0" dependencies: - axios: "npm:1.4.0" - moment-timezone: "npm:^0.5.31" - rrule: "npm:2.6.4" - uuid: "npm:^9.0.0" - checksum: 10c0/3bad842a9788a1b774849759d8d711e19920e4a3dfdd13ede36fac49972896d43cc8888c49ef9c9c09864f1c51f60566153c15746b2bb2491725067bae4fb8a3 + axios: "npm:1.7.7" + moment-timezone: "npm:^0.5.45" + rrule: "npm:2.8.1" + uuid: "npm:^10.0.0" + checksum: 10c0/15788f5a658eccb0b3ec7e692f8e882612a5e55926ab7d55e4540bdb88963e548f35388486a2d95e75caa939db840560ff4a159db46ffa509ec35df40c62b12c languageName: node linkType: hard @@ -9192,16 +9188,12 @@ __metadata: languageName: node linkType: hard -"rrule@npm:2.6.4": - version: 2.6.4 - resolution: "rrule@npm:2.6.4" +"rrule@npm:2.8.1": + version: 2.8.1 + resolution: "rrule@npm:2.8.1" dependencies: - luxon: "npm:^1.21.3" - tslib: "npm:^1.10.0" - dependenciesMeta: - luxon: - optional: true - checksum: 10c0/0f19a2b26fad9316c243fd8f8b8e3e172dae870ce31d52a438ec86de0c84095c0718cbb023cf088f51ffb89a52faa41ed1cd421cc795dff31ee29020348b5ad9 + tslib: "npm:^2.4.0" + checksum: 10c0/c9350620bbd57d0cdf99320b576121a2d7ed579fda4ae50891e2779c7dfd6dc7b174b558b6598adefb1f0e053549dc977740e1a265f77dcf0827aaeea60b45e7 languageName: node linkType: hard @@ -9509,13 +9501,11 @@ __metadata: languageName: node linkType: hard -"slate-react@npm:^0.107.1": - version: 0.107.1 - resolution: "slate-react@npm:0.107.1" +"slate-react@npm:^0.108.0": + version: 0.108.0 + resolution: "slate-react@npm:0.108.0" dependencies: "@juggle/resize-observer": "npm:^3.4.0" - "@types/is-hotkey": "npm:^0.1.8" - "@types/lodash": "npm:^4.14.200" direction: "npm:^1.0.4" is-hotkey: "npm:^0.2.0" is-plain-object: "npm:^5.0.0" @@ -9526,7 +9516,7 @@ __metadata: react: ">=18.2.0" react-dom: ">=18.2.0" slate: ">=0.99.0" - checksum: 10c0/8dbfbb95f3ac9c0295772f693a8670bccf3e1d5c4bb7f8bed6748f5b4ca335f6e416d9e63a81493ad1d0709e0d33a156cbc8b21bbd067f4d59f155c7511303b4 + checksum: 10c0/8c5aedf3d82842b504f448cdc632cf1725dcca6657a41f46f97906c886a53bf4e30d18ced9088cb664af41c5087dd8451fae206fce1dc3a248a7dc8800dd226c languageName: node linkType: hard @@ -10208,7 +10198,7 @@ __metadata: checksum: 10c0/fdc92bb7b18b31c0e76f8ec4f98d07236b09590fd6578e587ad024792c8b2235d65125a8fd007fa47a84400f84ceccbf33f24e5198d953249e7204f4cef3517c languageName: node linkType: hard - + "tslib@npm:^1.10.0": version: 1.14.1 resolution: "tslib@npm:1.14.1" @@ -10439,6 +10429,13 @@ __metadata: languageName: node linkType: hard +"undici-types@npm:~6.19.2": + version: 6.19.8 + resolution: "undici-types@npm:6.19.8" + checksum: 10c0/078afa5990fba110f6824823ace86073b4638f1d5112ee26e790155f481f2a868cc3e0615505b6f4282bdf74a3d8caad715fd809e870c2bb0704e3ea6082f344 + languageName: node + linkType: hard + "unicode-canonical-property-names-ecmascript@npm:^2.0.0": version: 2.0.0 resolution: "unicode-canonical-property-names-ecmascript@npm:2.0.0" @@ -10572,21 +10569,21 @@ __metadata: languageName: node linkType: hard -"uuid@npm:^8.3.2": - version: 8.3.2 - resolution: "uuid@npm:8.3.2" +"uuid@npm:^10.0.0": + version: 10.0.0 + resolution: "uuid@npm:10.0.0" bin: uuid: dist/bin/uuid - checksum: 10c0/bcbb807a917d374a49f475fae2e87fdca7da5e5530820ef53f65ba1d12131bd81a92ecf259cc7ce317cbe0f289e7d79fdfebcef9bfa3087c8c8a2fa304c9be54 + checksum: 10c0/eab18c27fe4ab9fb9709a5d5f40119b45f2ec8314f8d4cf12ce27e4c6f4ffa4a6321dc7db6c515068fa373c075b49691ba969f0010bf37f44c37ca40cd6bf7fe languageName: node linkType: hard -"uuid@npm:^9.0.0": - version: 9.0.1 - resolution: "uuid@npm:9.0.1" +"uuid@npm:^8.3.2": + version: 8.3.2 + resolution: "uuid@npm:8.3.2" bin: uuid: dist/bin/uuid - checksum: 10c0/1607dd32ac7fc22f2d8f77051e6a64845c9bce5cd3dd8aa0070c074ec73e666a1f63c7b4e0f4bf2bc8b9d59dc85a15e17807446d9d2b17c8485fbc2147b27f9b + checksum: 10c0/bcbb807a917d374a49f475fae2e87fdca7da5e5530820ef53f65ba1d12131bd81a92ecf259cc7ce317cbe0f289e7d79fdfebcef9bfa3087c8c8a2fa304c9be54 languageName: node linkType: hard