From e7e2faaf2e6dd5a7cb6e2656ada65d1359621eef Mon Sep 17 00:00:00 2001 From: silentsoft Date: Sun, 6 Mar 2022 23:57:28 +0900 Subject: [PATCH 01/11] Add Jest configuration --- config-overrides.js | 16 ++++++ .../react/utils/UniformedResourceNameUtils.js | 2 +- .../utils/UniformedResourceNameUtils.test.js | 42 ++++++++++++++ src/main/react/utils/Utils.js | 4 ++ src/main/react/utils/Utils.test.js | 55 +++++++++++++++++++ 5 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 src/main/react/utils/UniformedResourceNameUtils.test.js create mode 100644 src/main/react/utils/Utils.test.js diff --git a/config-overrides.js b/config-overrides.js index 22d67e9..f48dc59 100644 --- a/config-overrides.js +++ b/config-overrides.js @@ -12,6 +12,22 @@ module.exports = { paths.appBuild = path.resolve(__dirname, 'target/classes/public'); return paths; }, + jest: function(config) { + config.rootDir = path.resolve(__dirname, 'src/main/react'); + config.roots = [ + '' + ]; + config.testMatch = [ + '/**/__tests__/**/*.{js,jsx,ts,tsx}', + '/**/*.{spec,test}.{js,jsx,ts,tsx}' + ]; + config.collectCoverage = true; + config.collectCoverageFrom = [ + '/**/*.{js,jsx,ts,tsx}' + ]; + config.coverageDirectory = path.resolve(__dirname, 'coverage'); + return config; + }, webpack: override( addPostcssPlugins([ require('tailwindcss'), diff --git a/src/main/react/utils/UniformedResourceNameUtils.js b/src/main/react/utils/UniformedResourceNameUtils.js index 3a6a036..206f3a4 100644 --- a/src/main/react/utils/UniformedResourceNameUtils.js +++ b/src/main/react/utils/UniformedResourceNameUtils.js @@ -2,7 +2,7 @@ source : src/main/java/org/silentsoft/hits/utils/UniformedResourceNameUtils.java */ export const normalize = (uri) => { - if (uri.length === 0) { + if (uri == null || uri.length === 0) { return ""; } diff --git a/src/main/react/utils/UniformedResourceNameUtils.test.js b/src/main/react/utils/UniformedResourceNameUtils.test.js new file mode 100644 index 0000000..906c83d --- /dev/null +++ b/src/main/react/utils/UniformedResourceNameUtils.test.js @@ -0,0 +1,42 @@ +/* +source : src/test/java/org/silentsoft/hits/utils/UniformedResourceNameUtilsTest.java + */ +import {normalize} from "./UniformedResourceNameUtils"; + +test("normalize", () => { + expect(normalize(null)).toBe(""); + expect(normalize(undefined)).toBe(""); + expect(normalize("")).toBe(""); + expect(normalize("/")).toBe(""); + expect(normalize("http:/")).toBe(""); + expect(normalize("http://")).toBe(""); + + expect(normalize("github.com/silentsoft/hits")).toBe("github.com/silentsoft/hits"); + expect(normalize("github.com/silentsoft/hits/")).toBe("github.com/silentsoft/hits"); + expect(normalize("/github.com/silentsoft/hits")).toBe("github.com/silentsoft/hits"); + expect(normalize("/github.com/silentsoft/hits/")).toBe("github.com/silentsoft/hits"); + expect(normalize("github.com/silentsoft/hits?")).toBe("github.com/silentsoft/hits"); + expect(normalize("github.com/silentsoft/hits?a=b")).toBe("github.com/silentsoft/hits?a=b"); + expect(normalize("github.com/silentsoft/hits#")).toBe("github.com/silentsoft/hits"); + expect(normalize("github.com/silentsoft/hits#readme")).toBe("github.com/silentsoft/hits"); + + expect(normalize("http:/github.com/silentsoft/hits")).toBe("github.com/silentsoft/hits"); + expect(normalize("http://github.com/silentsoft/hits")).toBe("github.com/silentsoft/hits"); + expect(normalize("https:/github.com/silentsoft/hits")).toBe("github.com/silentsoft/hits"); + expect(normalize("https://github.com/silentsoft/hits")).toBe("github.com/silentsoft/hits"); + + expect(normalize("https://github.com/silentsoft/hits########")).toBe("github.com/silentsoft/hits"); + expect(normalize("https://github.com/silentsoft/hits????####")).toBe("github.com/silentsoft/hits"); + expect(normalize("https://github.com/silentsoft/hits??//####")).toBe("github.com/silentsoft/hits"); + expect(normalize("https://github.com/silentsoft/hits#?//####")).toBe("github.com/silentsoft/hits"); + expect(normalize("https://github.com/silentsoft/hits????????")).toBe("github.com/silentsoft/hits"); + expect(normalize("https://github.com/silentsoft/hits////////")).toBe("github.com/silentsoft/hits"); + expect(normalize("https://github.com/silentsoft/hits//??//??")).toBe("github.com/silentsoft/hits"); + expect(normalize("https://github.com/silentsoft/hits??//??//")).toBe("github.com/silentsoft/hits"); + + expect(normalize("https://한글도메인.com/가나다라")).toBe("한글도메인.com/가나다라"); + expect(normalize("https://한글도메인.com/가나다라/")).toBe("한글도메인.com/가나다라"); + expect(normalize("https://한글도메인.com/가나다라#")).toBe("한글도메인.com/가나다라"); + expect(normalize("https://한글도메인.com/가나다라?")).toBe("한글도메인.com/가나다라"); + expect(normalize("https://한글도메인.com/가나다라?a=b")).toBe("한글도메인.com/가나다라?a=b"); +}); \ No newline at end of file diff --git a/src/main/react/utils/Utils.js b/src/main/react/utils/Utils.js index 9531bc3..269ac61 100644 --- a/src/main/react/utils/Utils.js +++ b/src/main/react/utils/Utils.js @@ -1,5 +1,9 @@ export default class Utils { static toQueryString(object) { + if (object == null) { + return ""; + } + let queryString = ""; const defaultViewType = "total"; diff --git a/src/main/react/utils/Utils.test.js b/src/main/react/utils/Utils.test.js new file mode 100644 index 0000000..91b19a0 --- /dev/null +++ b/src/main/react/utils/Utils.test.js @@ -0,0 +1,55 @@ +import Utils from "./Utils"; + +test("toQueryString", () => { + expect(Utils.toQueryString(null)).toBe(""); + expect(Utils.toQueryString(undefined)).toBe(""); + expect(Utils.toQueryString({})).toBe(""); + expect(Utils.toQueryString({foo: "bar"})).toBe(""); + + expect(Utils.toQueryString({view: "today-total"})).toBe("?view=today-total"); + expect(Utils.toQueryString({style: "flat-square"})).toBe("?style=flat-square"); + expect(Utils.toQueryString({label: "foo"})).toBe("?label=foo"); + expect(Utils.toQueryString({extraCount: 1000})).toBe("?extraCount=1000"); + expect(Utils.toQueryString({color: "black"})).toBe("?color=black"); + expect(Utils.toQueryString({color: "123"})).toBe("?color=123"); + expect(Utils.toQueryString({color: "#123"})).toBe("?color=123"); + expect(Utils.toQueryString({color: "123456"})).toBe("?color=123456"); + expect(Utils.toQueryString({color: "#123456"})).toBe("?color=123456"); + expect(Utils.toQueryString({labelColor: "black"})).toBe("?labelColor=black"); + expect(Utils.toQueryString({labelColor: "123"})).toBe("?labelColor=123"); + expect(Utils.toQueryString({labelColor: "#123"})).toBe("?labelColor=123"); + expect(Utils.toQueryString({labelColor: "123456"})).toBe("?labelColor=123456"); + expect(Utils.toQueryString({labelColor: "#123456"})).toBe("?labelColor=123456"); + expect(Utils.toQueryString({link: "https://hits.sh"})).toBe("?link=https%3A%2F%2Fhits.sh"); + expect(Utils.toQueryString({link: ["https://hits.sh", "https://hits.sh"]})).toBe("?link=https%3A%2F%2Fhits.sh&link=https%3A%2F%2Fhits.sh"); + expect(Utils.toQueryString({logo: "logo"})).toBe("?logo=logo"); + expect(Utils.toQueryString({logoWidth: 48})).toBe("?logoWidth=48"); + expect(Utils.toQueryString({ + view: "today-total", + style: "flat-square", + label: "foo", + extraCount: 1000, + color: "black", + labelColor: "black", + link: "https://hits.sh", + logo: "logo", + logoWidth: 48 + })).toBe("?view=today-total&style=flat-square&label=foo&extraCount=1000&color=black&labelColor=black&link=https%3A%2F%2Fhits.sh&logo=logo&logoWidth=48"); +}); + +test("toQueryString with default value", () => { + expect(Utils.toQueryString({view: "total"})).toBe(""); + expect(Utils.toQueryString({style: "flat"})).toBe(""); + expect(Utils.toQueryString({label: "hits"})).toBe(""); + expect(Utils.toQueryString({color: "4c1"})).toBe(""); + expect(Utils.toQueryString({color: "#4c1"})).toBe(""); + expect(Utils.toQueryString({color: "44cc11"})).toBe(""); + expect(Utils.toQueryString({color: "#44cc11"})).toBe(""); + expect(Utils.toQueryString({color: "brightgreen"})).toBe(""); + expect(Utils.toQueryString({labelColor: "555"})).toBe(""); + expect(Utils.toQueryString({labelColor: "#555"})).toBe(""); + expect(Utils.toQueryString({labelColor: "555555"})).toBe(""); + expect(Utils.toQueryString({labelColor: "#555555"})).toBe(""); + expect(Utils.toQueryString({labelColor: "grey"})).toBe(""); + expect(Utils.toQueryString({labelColor: "gray"})).toBe(""); +}); \ No newline at end of file From 3d4560b8499b2ca72e2f4f15fe9e4f39ac013b6f Mon Sep 17 00:00:00 2001 From: silentsoft Date: Mon, 7 Mar 2022 15:09:02 +0900 Subject: [PATCH 02/11] Add Travis settings --- .gitignore | 3 + .nvmrc | 1 + .travis.yml | 26 ++++++ pom.xml | 84 +++++++++++++++---- .../org/silentsoft/oss/NoticeFileTest.java | 8 +- 5 files changed, 100 insertions(+), 22 deletions(-) create mode 100644 .nvmrc create mode 100644 .travis.yml diff --git a/.gitignore b/.gitignore index a345ae0..2b6042d 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,9 @@ build/ # testing /coverage +# wrapper +/node + # production /build diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..0b77208 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +16.14.0 \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..d0e68f5 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,26 @@ +jobs: + include: + - language: node_js + - language: java + cache: + directories: + - "$HOME/.m2" + before_install: + # "node" is an alias for the latest version + - nvm install node + script: + # the following command line builds the project, runs the tests with coverage and then execute the SonarCloud analysis + - mvn clean verify sonar:sonar -Pcoverage -DskipRedundantTests -Dsonar.projectKey=silentsoft_hits -Dsonar.sources=src/main/java,src/main/react + +git: + depth: false + +addons: + sonarcloud: + organization: "silentsoft" + token: + secure: "O9I2+3DaVmoEuRblOn+Wv2EQSWlzCoujmBxBYmBlAudvmFXIPocPcHJXJT2hnLdKJDhVBv922JRuawwLSqVnGnNZuN9NWqDZbN79lEOXA6789V3Dd2M6LZhbEzRzNcCh2oqv8T8estta6Cf7po7CNkplZ1lbCxnKmzyv6Vuw8pqSf5UgYfZoMg+noL44qigt2d5RB3eZK9zkvNCbG8y+oHg9jmEsXJ+RVUEzHhKQwly5rlGiTqBf7yqD79hgSCmFDy7hUWpf/s8zLJQm0TdeiMn0lC82nWG0tjGwUMPbK98dhxEzFJL6NJTo4BkuDDHGAkshexwA0I7kTaWIsbHdgeFqKLy1fCz9QeL1fotzq4Cbcn1WyuvmWzBflRiNaHuKuh21Exk6iHCbe742eQu7BLpzoyhdUyUkIlaUTaJQ3AbX1oCsKCzdb5z0p9a6sMjNpSVuthXeQnc5cE+8MJnxE/kv6Bx/+ptDSWkoFkHZm+RPVLp7eNlct3NfXNcDyXAvKfRCwo+8uLi0BskKQE3Qb0rnxG1usJiAmMBc83z30A6gKPmEF6kvowl6Ej269bCL/MI1PcPiTsVEdW147oPtKzkyccYa+UKhPbER/1iJaFpTUQeyBHWK9A+yjo7V7Bsw2oyHzqukhKPoA1A504nWmuquyTJ0MvjFGbbMfYop1hE=" + +notifications: + slack: + secure: "wnKJB3Yaxaw77Ls5WiDSNzrHbNmUhdgiigCiFk121UncDfX1zBhlUAze05v7FllnY4/iHTeJoXRCDUMHfj7KPjLXutaoqa0YidpVQxWtxPJMbajBesSTjuOwjjrl5LDCWriNnRhUfcSDZwoDJSVH6ArhaXcftUX0yyHHqnr2TNc60+hfSScJNolmmgy9ht14D/7Pi2p92CmbNEVf1+sDxRnjtanV0Sk9neMTDcNuLvJYxnf+Y7mJtSgjkQsz/3UgLYDbEWmpVynONuSOu05sAG0zWTLZIXJPGL7+W2mW3VSP85wF8APVvLDB9lafY129Q1XnTUaFdu6F3ZMu9L7+CIbXhDspoikjonkhthCJDm6KtJ4+KBxLN03GDwqXy/rVpk6ER6i9jOp5bwrt7BHCbin5zYpsoMWZdpj1TyeI8zRDJdLm0GIxRxx08jAxHJSphnt+dzR7qMIymD9nv12rYVCejcosGooLWRBD+EtNSwdD/8Ne3MnfhN8m9cuiZHtcNKPjcS1Uk6m0GWU4pL4ZP0An+0cbfcnoMWZiG3Ic3iA9Ho9bSCfm1c1uNV/lwJfV4/hfx/8tiAjzoijTFTN8oIBXqsMnnDsPax1OXDGZvKCM8mWHtSmPLcIJMErY6fwEJgMnlr752RPA2ZQHR+tvT3eZa0PZonOqDPl30bt21Is=" \ No newline at end of file diff --git a/pom.xml b/pom.xml index 2c7fd13..13f293e 100644 --- a/pom.xml +++ b/pom.xml @@ -61,6 +61,36 @@ ${project.artifactId} + + coverage + + dev + build:dev + + + + + org.jacoco + jacoco-maven-plugin + 0.8.5 + + + prepare-agent + + prepare-agent + + + + report + + report + + + + + + + @@ -77,40 +107,62 @@ + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.0 + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.2 + + + org.apache.maven.plugins + maven-failsafe-plugin + 2.22.2 + org.springframework.boot spring-boot-maven-plugin - org.codehaus.mojo - exec-maven-plugin - 1.6.0 + com.github.eirslett + frontend-maven-plugin + 1.9.1 + + v16.14.0 + + + install-node-and-npm + + install-node-and-npm + + generate-resources + npm-install - initialize - exec + npm + generate-resources - npm - - install - + install npm-run-build - process-resources - exec + npm + generate-resources - npm - - run - ${npmBuildScript} - + run ${npmBuildScript} diff --git a/src/test/java/org/silentsoft/oss/NoticeFileTest.java b/src/test/java/org/silentsoft/oss/NoticeFileTest.java index 7db6b41..46e127a 100644 --- a/src/test/java/org/silentsoft/oss/NoticeFileTest.java +++ b/src/test/java/org/silentsoft/oss/NoticeFileTest.java @@ -3,8 +3,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.DisabledOnOs; -import org.junit.jupiter.api.condition.OS; +import org.junit.jupiter.api.condition.DisabledIfSystemProperty; import java.io.*; import java.nio.file.Paths; @@ -16,7 +15,7 @@ public class NoticeFileTest { @Test - @DisabledOnOs(OS.WINDOWS) + @DisabledIfSystemProperty(named = "skipRedundantTests", matches = "true") public void noticeFileTest() throws Exception { NoticeFileGenerator.NoticeFileBuilder noticeBuilder = NoticeFileGenerator.newInstance("Hits", "silentsoft.org"); @@ -24,9 +23,6 @@ public void noticeFileTest() throws Exception { addBackendLibraries(noticeBuilder); String markdown = noticeBuilder.generate(); - System.out.println("--------START OF THE NOTICE FILE--------"); - System.out.println(markdown); - System.out.println("---------END OF THE NOTICE FILE---------"); StringBuilder stringBuilder = new StringBuilder(); try (FileReader fileReader = new FileReader(Paths.get(System.getProperty("user.dir"), "NOTICE.md").toFile()); From 192ee833b455a8461b5d275c7f1f27a2b8e084b1 Mon Sep 17 00:00:00 2001 From: silentsoft Date: Tue, 8 Mar 2022 15:06:02 +0900 Subject: [PATCH 03/11] Update Travis and Sonar settings --- .travis.yml | 8 ++++---- config-overrides.js | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index d0e68f5..427220b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,11 +6,11 @@ jobs: directories: - "$HOME/.m2" before_install: - # "node" is an alias for the latest version - - nvm install node + - nvm install $(< .nvmrc) script: - # the following command line builds the project, runs the tests with coverage and then execute the SonarCloud analysis - - mvn clean verify sonar:sonar -Pcoverage -DskipRedundantTests -Dsonar.projectKey=silentsoft_hits -Dsonar.sources=src/main/java,src/main/react + - npm install + - npm test + - mvn clean verify sonar:sonar -Pcoverage -DskipRedundantTests -Dsonar.projectKey=silentsoft_hits -Dsonar.sources=src/main/java,src/main/react -Dsonar.javascript.lcov.reportPaths=coverage/lcov.info git: depth: false diff --git a/config-overrides.js b/config-overrides.js index f48dc59..5a54442 100644 --- a/config-overrides.js +++ b/config-overrides.js @@ -23,7 +23,8 @@ module.exports = { ]; config.collectCoverage = true; config.collectCoverageFrom = [ - '/**/*.{js,jsx,ts,tsx}' + '**/*.{js,jsx,ts,tsx}', + '!**/*.d.ts' ]; config.coverageDirectory = path.resolve(__dirname, 'coverage'); return config; From ca863c5ea81417c2e033ae376a39e22f4978e789 Mon Sep 17 00:00:00 2001 From: silentsoft Date: Wed, 16 Mar 2022 15:37:05 +0900 Subject: [PATCH 04/11] Remove terminal component --- NOTICE.md | 4 - src/main/react/App.js | 8 +- src/main/react/terminal/Field.js | 283 ------------------ src/main/react/terminal/Terminal.css | 111 ------- src/main/react/terminal/Terminal.js | 65 ---- .../org/silentsoft/oss/NoticeFileTest.java | 2 - 6 files changed, 1 insertion(+), 472 deletions(-) delete mode 100644 src/main/react/terminal/Field.js delete mode 100644 src/main/react/terminal/Terminal.css delete mode 100644 src/main/react/terminal/Terminal.js diff --git a/NOTICE.md b/NOTICE.md index 03b12d1..87a82ed 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -1,10 +1,6 @@ # Hits Copyright (c) silentsoft.org. All rights reserved. -__Terminal Style Portfolio in ReactJS by Jacob Lockett__ - * https://codepen.io/HuntingHawk/pen/rNaEZxW - * MIT License - __combined-stream 1.0.8__ * http://debuggable.com/ * MIT License diff --git a/src/main/react/App.js b/src/main/react/App.js index f7c8ef7..99e4a8f 100644 --- a/src/main/react/App.js +++ b/src/main/react/App.js @@ -1,7 +1,6 @@ import Header from "./Header"; import Content from "./Content"; import Footer from "./Footer"; -import Terminal from "./terminal/Terminal"; export default function App() { return ( @@ -18,12 +17,7 @@ export default function App() { -
-
-
- -
- +
diff --git a/src/main/react/terminal/Field.js b/src/main/react/terminal/Field.js deleted file mode 100644 index 116c54b..0000000 --- a/src/main/react/terminal/Field.js +++ /dev/null @@ -1,283 +0,0 @@ -import React from 'react'; -import {normalize} from '../utils/UniformedResourceNameUtils'; -import Utils from "../utils/Utils"; - -class Field extends React.Component { - constructor(props) { - super(props) - - this.state = { - commandHistory: [], - commandHistoryIndex: 0, - fieldHistory: [], - userInput: '' - } - - this.error = { - INVALID_URL: 0x00, - NOT_SUPPORTED_FLAG: 0x01, - INVALID_FLAG_VALUE: 0x02, - NOT_RECOGNIZED: 0x03 - } - - this.availableFlags = [{ - flag: '--view', - purpose: 'Set the hits view type. Available view types are: \'total\' and \'today-total\'. (Default is \'total\')', - supported: ['total', 'today-total'] - }, { - flag: '--extra-count', - purpose: 'Set the number of additions to hits. This is useful when migrating counts.' - }, { - flag: '--style', - purpose: 'Set the style of the badge. Available styles are: \'flat\', \'flat-square\', \'for-the-badge\', \'plastic\' and \'social\'. (Default is \'flat\')', - supported: ['flat', 'flat-square', 'for-the-badge', 'plastic', 'social'] - }, { - flag: '--label', - purpose: 'Set the text of the left side of the badge. (Default is \'hits\')' - }, { - flag: '--color', - purpose: 'Set the color of the right side of the badge. (Default is \'#4c1\')' - }, { - flag: '--label-color', - purpose: 'Set the color of the left side of the badge. (Default is \'#555\')' - }, { - flag: '--link', - purpose: 'Set the url(s) to link to the badge.' - }, { - flag: '--logo', - purpose: 'Set the logo to use. (simple-icons slug or data:image/svg+xml;base64,..)' - }, { - flag: '--logo-width', - purpose: 'Set the custom logo width.' - }] - - this.handleTyping = this.handleTyping.bind(this) - this.handleInputEvaluation = this.handleInputEvaluation.bind(this) - this.handleInputExecution = this.handleInputExecution.bind(this) - } - componentDidMount() { - this.handleInputEvaluation('help') - } - componentDidUpdate() { - const field = document.querySelector('#field') - - field.scrollTop = field.scrollHeight - } - handleTyping(e) { - const { key } = e - - if (key === 'Escape') { - this.setState({ userInput: '' }) - } else if (key === 'ArrowUp') { - const { commandHistory, commandHistoryIndex } = this.state - const upperLimit = commandHistoryIndex >= commandHistory.length - - if (!upperLimit) { - this.setState(state => ({ - commandHistoryIndex: state.commandHistoryIndex += 1, - userInput: state.commandHistory[state.commandHistoryIndex - 1] - })) - } - } else if (key === 'ArrowDown') { - const { commandHistory, commandHistoryIndex } = this.state - const lowerLimit = commandHistoryIndex === 0 - - if (!lowerLimit) { - this.setState(state => ({ - commandHistoryIndex: state.commandHistoryIndex -= 1, - userInput: state.commandHistory[state.commandHistoryIndex - 1] || '' - })) - } - } else if (key === 'Enter') { - const userInput = this.state.userInput.trim() - - if (userInput.length) { - this.setState(state => ({ - commandHistory: userInput === '' ? state.commandHistory : [userInput, ...state.commandHistory], - commandHistoryIndex: 0, - fieldHistory: [...state.fieldHistory, {text: userInput, isCommand: true}], - userInput: '' - }), () => this.handleInputEvaluation(userInput)) - } else { - this.setState(state => ({ - fieldHistory: [...state.fieldHistory, {isCommand: true}], - userInput: '' - })) - } - } - } - handleInputEvaluation(input) { - const { handleInputExecution } = this - - const cleanedInput = input.trim() - const dividedInput = cleanedInput.match(/([^\s]+)(\"(.*?)\")|([^\s]+)/g); - const parsedCommand = dividedInput[0].toLowerCase() - const parsedParameters = dividedInput.slice(1).filter(s => s[0] !== '-') - const parsedFlags = dividedInput.slice(1).filter(s => s[0] === '-') - - return handleInputExecution(parsedCommand, parsedParameters, parsedFlags) - } - handleInputExecution(command, parameters = [], flags = []) { - const { error } = this; - - if (command === 'help' || command === '/help' || command === '-help' || command === '--help' || command === '?' || command === '/?') { - return this.setState(state => ({ - fieldHistory: [...state.fieldHistory, { - text: [ - 'Usage:', - '', - '\u00A0\u00A0url [parameters...]', - '', - 'Available parameters:', - '', - ...this.availableFlags - .map(({ flag, purpose }) => `\u00A0\u00A0${flag}${Array.from({length: 15 - flag.length}, x => '\u00A0').join('')}${purpose}`), - '', - 'Example usages:', - '', - '\u00A0\u00A0https://github.com/silentsoft/hits', - '\u00A0\u00A0https://github.com/silentsoft/hits --view=today-total', - '\u00A0\u00A0https://github.com/silentsoft/hits --extra-count=1000' - ], - hasBuffer: true - }] - })) - } else if (command === 'cls' || command === 'clear') { - return this.setState({fieldHistory: []}) - } else if (command === 'exit' || command === 'quit') { - return window.location.href = 'https://github.com/silentsoft/hits' - } else if (command === 'rank') { - return this.setState(state => ({ - fieldHistory: [...state.fieldHistory, {text: `This feature is not yet available.`, hasBuffer: true}] - })) - } else if (command.length && command.includes('.') && command[0] !== '.' && command[command.length-1] !== '.') { - const urn = normalize(command); - - if (parameters.length) { - return this.setState(state => ({fieldHistory: [...state.fieldHistory, this.giveError(error.NOT_RECOGNIZED, parameters)]})) - } - - const notSupportedParameters = flags.filter(flag => - this.availableFlags.filter(p => - p.flag === flag.split('=')[0] - ).length === 0 - ); - if (notSupportedParameters.length) { - return this.setState(state => ({fieldHistory: [...state.fieldHistory, this.giveError(error.NOT_SUPPORTED_FLAG, notSupportedParameters[0].split('=')[0])]})); - } - - const invalidParameterValues = flags.filter(flag => - this.availableFlags.filter(p => - p.flag === flag.split('=')[0] && (p.supported === undefined || (p.supported && p.supported.includes(flag.split('=')[1]))) - ).length === 0 - ); - if (invalidParameterValues.length) { - return this.setState(state => ({fieldHistory: [...state.fieldHistory, this.giveError(error.INVALID_FLAG_VALUE, invalidParameterValues[0].split('=')[1])]})); - } - - const query = this.toQueryString(flags) - return this.setState(state => ({ - fieldHistory: [...state.fieldHistory, {text: [ - 'Markdown:', - '', - `\u00A0\u00A0[![Hits](https://hits.sh/${urn}.svg${query})](https://hits.sh/${urn}/)`, - '', - 'HTML:', - '', - `\u00A0\u00A0Hits`, - '', - 'Image Link:', - '', - `\u00A0\u00A0https://hits.sh/${urn}.svg${query}` - ], hasBuffer: true}] - })) - } else { - return this.setState(state => ({fieldHistory: [...state.fieldHistory, this.giveError(error.INVALID_URL, command)]})) - } - } - toQueryString(flags) { - const camel = (value) => { - if (value.includes('-')) { - return value.toLowerCase().replace(/[-][a-z]/g, capture => capture.replace('-', '').toUpperCase()); - } - return value; - } - const key = (flag) => { - return flag.split('=')[0].replace('--', ''); - } - const value = (flag) => { - if (flag.includes('=')) { - return flag.substring(flag.indexOf('=')+1, flag.length).replace(/[\"]/g, ''); - } - return ''; - } - return Utils.toQueryString(flags.reduce((acc, flag) => { - if (key(flag) === 'link') { - if (Array.isArray(acc[camel(key(flag))])) { - if (acc[camel(key(flag))].length < 2) { - acc[camel(key(flag))].push(value(flag)); - } - } else { - acc[camel(key(flag))] = [value(flag)]; - } - } else { - acc[camel(key(flag))] = value(flag); - } - return acc; - }, {})); - } - giveError(type, extra) { - const field = { text: '', isError: true, hasBuffer: true} - const { error } = this; - - if (type === error.INVALID_URL) { - field.text = `'${extra}' is an invalid url. Check the spelling and try again. If you don't know what commands are recognized, type HELP.`; - } else if (type === error.NOT_SUPPORTED_FLAG) { - field.text = `The flag '${extra}' is not supported. Check the spelling and try again. If you don't know what commands are recognized, type HELP.`; - } else if (type === error.INVALID_FLAG_VALUE) { - field.text = `'${extra}' is an invalid flag value. Check the spelling and try again. If you don't know what commands are recognized, type HELP.`; - } else if (type === error.NOT_RECOGNIZED) { - field.text = `${extra} : The term or expression '${extra}' is not recognized. Check the spelling and try again. If you don't know what commands are recognized, type HELP.`; - } - - return field - } - render() { - const { theme } = this.props - const { fieldHistory, userInput } = this.state - - return
- {fieldHistory.map(({ text, isCommand, isError, hasBuffer }, index) => { - if (Array.isArray(text)) { - return - } - - return - })} - this.setState(state => ({userInput: e.target.value}))} /> -
- } -} - -const Text = ({ input, isCommand, isError, hasBuffer }) => <> -
- {isCommand &&
Hits>
} - {input} -
- {hasBuffer &&
} - -const MultiText = ({ input, isError, hasBuffer }) => <> - {input.map((s, index) => )} - {hasBuffer &&
} - -const UserText = ({ input, theme, typingHandler, onChangeHandler }) =>
-
Hits>
- -
- -export default Field; \ No newline at end of file diff --git a/src/main/react/terminal/Terminal.css b/src/main/react/terminal/Terminal.css deleted file mode 100644 index ee5d279..0000000 --- a/src/main/react/terminal/Terminal.css +++ /dev/null @@ -1,111 +0,0 @@ -#app { - font-family: Roboto Mono; - height: 100vh; - display: flex; - justify-content: center; - align-items: center; -} -#terminal { - width: 100vw; - height: 100vh; -} -#window { - height: 40px; - display: flex; - align-items: center; - padding: 0 20px; - cursor: default; -} -.btn { - margin-right: 8px; - border: none; - height: 12px; - width: 12px; - padding: 0; - border-radius: 50%; - box-shadow: 0 2px 2px #33333375; -} -.red { - background-color: #FF4136; -} -.error { - color: #FF4136; -} -.yellow { - background-color: #FFDC00; -} -.info { - color: #FFDC00; -} -.green { - background-color: #2ECC40; -} -#title, -#field, -#field > * > input { - outline: none; - font-size: .85rem; -} -#title { - height: 20px; - margin-left: auto; -} -#field { - height: calc(100% - 40px); - padding: 5px; - overflow: auto; -} -#field::-webkit-scrollbar { - width: 10px; -} -#field.dark::-webkit-scrollbar-thumb { - background-color: #333333; -} -#field.light::-webkit-scrollbar-thumb { - background-color: #ACA9BB; -} -#field > div { - min-height: 20px; - width: 100%; - cursor: default; -} -#field > * > input { - border: 0; - margin: 0; - padding: 0; - width: 100%; -} -#input-container { - display: flex; -} -#query, -#cursor { - display: inline-block; -} -#query { - margin-right: 10px; - white-space: pre-line; -} -#cursor { - position: relative; - bottom: -2px; - left: 2px; - width: .5rem; - height: 3px; -} -@keyframes blink-dark { - 0%, 100% { - background-color: #F4F4F4; - } - 50% { - background-color: transparent; - } -} -@keyframes blink-light { - 0%, 100% { - background-color: #474554; - } - 50% { - background-color: transparent; - } -} \ No newline at end of file diff --git a/src/main/react/terminal/Terminal.js b/src/main/react/terminal/Terminal.js deleted file mode 100644 index 9a65abd..0000000 --- a/src/main/react/terminal/Terminal.js +++ /dev/null @@ -1,65 +0,0 @@ -import React, {useEffect, useState} from 'react'; - -import './Terminal.css'; -import Field from './Field'; - -export default function Terminal() { - const [theme, setTheme] = useState('dark'); - const themeVars = theme === 'dark' ? { - app: {backgroundColor: '#ffffff'}, - terminal: {boxShadow: '0 2px 5px #111'}, - window: {backgroundColor: '#1f1f1f', color: '#F4F4F4'}, - field: {backgroundColor: '#222222', color: '#F4F4F4', fontWeight: 'normal'}, - cursor: {animation : '1.02s blink-dark step-end infinite'} - } : { - app: {backgroundColor: '#ACA9BB'}, - terminal: {boxShadow: '0 2px 5px #33333375'}, - window: {backgroundColor: '#5F5C6D', color: '#E3E3E3'}, - field: {backgroundColor: '#E3E3E3', color: '#474554', fontWeight: 'bold'}, - cursor: {animation : '1.02s blink-light step-end infinite'} - } - - const [title, setTitle] = useState('hits.sh'); - const handleClose = () => { - window.location.href = 'https://github.com/silentsoft/hits'; - } - const handleMinimize = () => { - document.documentElement.scrollTop = 0; - } - const handleMaximize = () => { - document.documentElement.scrollTop = document.documentElement.scrollHeight; - focus(); - } - - const focus = () => { - document.querySelector('#field-input').focus(); - } - - const scrollHandler = () => { - if (document.documentElement.scrollTop === document.documentElement.scrollHeight - document.documentElement.clientHeight) { - focus(); - } - } - - useEffect(() => { - window.addEventListener('scroll', scrollHandler); - - return () => { - window.removeEventListener('scroll', scrollHandler); - } - }, []) - - return ( -
-
-
-
- -
-
- ); -} \ No newline at end of file diff --git a/src/test/java/org/silentsoft/oss/NoticeFileTest.java b/src/test/java/org/silentsoft/oss/NoticeFileTest.java index 46e127a..285c8a3 100644 --- a/src/test/java/org/silentsoft/oss/NoticeFileTest.java +++ b/src/test/java/org/silentsoft/oss/NoticeFileTest.java @@ -37,8 +37,6 @@ public void noticeFileTest() throws Exception { } private void addFrontendLibraries(NoticeFileGenerator.NoticeFileBuilder noticeBuilder) throws Exception { - noticeBuilder.addLibrary("Terminal Style Portfolio in ReactJS", "by Jacob Lockett", "https://codepen.io/HuntingHawk/pen/rNaEZxW", License.of("MIT")); - File directory = Paths.get(System.getProperty("user.dir")).toFile(); StringBuilder commandBuilder = new StringBuilder(); From 23ea4c4b4347f06b3255b62be2b8d2159fbc7417 Mon Sep 17 00:00:00 2001 From: silentsoft Date: Wed, 16 Mar 2022 15:39:15 +0900 Subject: [PATCH 05/11] Add auto focus feature --- src/main/react/Content.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/react/Content.js b/src/main/react/Content.js index 8ab18a9..aafb125 100644 --- a/src/main/react/Content.js +++ b/src/main/react/Content.js @@ -117,7 +117,7 @@ export default function Content(props) { - +
From 509fb9e4f9518e91df561adba11667ec2a5fd7e5 Mon Sep 17 00:00:00 2001 From: silentsoft Date: Thu, 17 Mar 2022 10:29:10 +0900 Subject: [PATCH 06/11] Add Jest test cases --- config-overrides.js | 10 +- package-lock.json | 782 ++++++++++++++++++ package.json | 4 + src/main/react/App.js | 27 +- src/main/react/App.test.js | 14 + src/main/react/Content.test.js | 60 ++ src/main/react/Dashboard.js | 10 +- src/main/react/Dashboard.test.js | 65 ++ src/main/react/Footer.test.js | 9 + src/main/react/Header.js | 2 +- src/main/react/Header.test.js | 16 + src/main/react/Home.js | 25 + src/main/react/Home.test.js | 10 + src/main/react/components/BadgeCard.js | 2 + src/main/react/components/BadgeCard.test.js | 44 + src/main/react/components/ColorPicker.js | 8 +- src/main/react/components/ColorPicker.test.js | 33 + src/main/react/components/SimpleDropdown.js | 4 +- .../react/components/SimpleDropdown.test.js | 30 + src/main/react/index.js | 18 +- src/main/react/setupTests.js | 17 + src/main/react/utils/Utils.test.js | 2 + 22 files changed, 1148 insertions(+), 44 deletions(-) create mode 100644 src/main/react/App.test.js create mode 100644 src/main/react/Content.test.js create mode 100644 src/main/react/Dashboard.test.js create mode 100644 src/main/react/Footer.test.js create mode 100644 src/main/react/Header.test.js create mode 100644 src/main/react/Home.js create mode 100644 src/main/react/Home.test.js create mode 100644 src/main/react/components/BadgeCard.test.js create mode 100644 src/main/react/components/ColorPicker.test.js create mode 100644 src/main/react/components/SimpleDropdown.test.js create mode 100644 src/main/react/setupTests.js diff --git a/config-overrides.js b/config-overrides.js index 5a54442..f024f30 100644 --- a/config-overrides.js +++ b/config-overrides.js @@ -17,6 +17,13 @@ module.exports = { config.roots = [ '' ]; + config.setupFilesAfterEnv = [ + '/setupTests.js' + ]; + config.transformIgnorePatterns = [ + '/node_modules/', + '\\.pnp\\.[^\\\/]+$' + ]; config.testMatch = [ '/**/__tests__/**/*.{js,jsx,ts,tsx}', '/**/*.{spec,test}.{js,jsx,ts,tsx}' @@ -24,7 +31,8 @@ module.exports = { config.collectCoverage = true; config.collectCoverageFrom = [ '**/*.{js,jsx,ts,tsx}', - '!**/*.d.ts' + '!**/*.d.ts', + '!index.{js,jsx,ts,tsx}', ]; config.coverageDirectory = path.resolve(__dirname, 'coverage'); return config; diff --git a/package-lock.json b/package-lock.json index 7d290d2..1d47ce6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,8 +21,12 @@ "react-scripts": "5.0.0" }, "devDependencies": { + "@testing-library/jest-dom": "^5.16.2", + "@testing-library/react": "^12.1.4", + "@testing-library/user-event": "^13.5.0", "autoprefixer": "^10.4.2", "customize-cra": "^1.0.0", + "jest-canvas-mock": "^2.3.1", "license-checker": "^25.0.1", "postcss": "^8.4.7", "react-app-rewired": "^2.2.1", @@ -3410,6 +3414,236 @@ "url": "https://github.com/sponsors/gregberge" } }, + "node_modules/@testing-library/dom": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.11.3.tgz", + "integrity": "sha512-9LId28I+lx70wUiZjLvi1DB/WT2zGOxUh46glrSNMaWVx849kKAluezVzZrXJfTKKoQTmEOutLes/bHg4Bj3aA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^4.2.0", + "aria-query": "^5.0.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.4.4", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@testing-library/dom/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@testing-library/dom/node_modules/aria-query": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.0.0.tgz", + "integrity": "sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/@testing-library/dom/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@testing-library/dom/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@testing-library/dom/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@testing-library/dom/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/dom/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/jest-dom": { + "version": "5.16.2", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.2.tgz", + "integrity": "sha512-6ewxs1MXWwsBFZXIk4nKKskWANelkdUehchEOokHsN8X7c2eKXGw+77aRV63UU8f/DTSVUPLaGxdrj4lN7D/ug==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.9.2", + "@types/testing-library__jest-dom": "^5.9.1", + "aria-query": "^5.0.0", + "chalk": "^3.0.0", + "css": "^3.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.5.6", + "lodash": "^4.17.15", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=8", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/aria-query": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.0.0.tgz", + "integrity": "sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@testing-library/jest-dom/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/react": { + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-12.1.4.tgz", + "integrity": "sha512-jiPKOm7vyUw311Hn/HlNQ9P8/lHNtArAx0PisXyFixDDvfl8DbD6EUdbshK5eqauvBSvzZd19itqQ9j3nferJA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "@testing-library/dom": "^8.0.0", + "@types/react-dom": "*" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/@testing-library/user-event": { + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.5.0.tgz", + "integrity": "sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, "node_modules/@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -3426,6 +3660,12 @@ "node": ">=10.13.0" } }, + "node_modules/@types/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==", + "dev": true + }, "node_modules/@types/babel__core": { "version": "7.1.18", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.18.tgz", @@ -3583,6 +3823,16 @@ "@types/istanbul-lib-report": "*" } }, + "node_modules/@types/jest": { + "version": "27.4.1", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.4.1.tgz", + "integrity": "sha512-23iPJADSmicDVrWk+HT58LMJtzLAnB2AgIzplQuq/bSrGaxCrlvRFjGbXmamnnk/mAmCdLStiGqggu28ocUyiw==", + "dev": true, + "dependencies": { + "jest-matcher-utils": "^27.0.0", + "pretty-format": "^27.0.0" + } + }, "node_modules/@types/json-schema": { "version": "7.0.9", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", @@ -3613,6 +3863,12 @@ "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.4.tgz", "integrity": "sha512-ReVR2rLTV1kvtlWFyuot+d1pkpG2Fw/XKE3PDAdj57rbM97ttSp9JZ2UsP+2EHTylra9cUf6JA7tGwW1INzUrA==" }, + "node_modules/@types/prop-types": { + "version": "15.7.4", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz", + "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==", + "dev": true + }, "node_modules/@types/q": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz", @@ -3628,6 +3884,26 @@ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" }, + "node_modules/@types/react": { + "version": "17.0.40", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.40.tgz", + "integrity": "sha512-UrXhD/JyLH+W70nNSufXqMZNuUD2cXHu6UjCllC6pmOQgBX4SGXOH8fjRka0O0Ee0HrFxapDD8Bwn81Kmiz6jQ==", + "dev": true, + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "17.0.13", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.13.tgz", + "integrity": "sha512-wEP+B8hzvy6ORDv1QBhcQia4j6ea4SFIBttHYpXKPFZRviBvknq0FRh3VrIxeXUmsPkwuXVZrVGG7KUVONmXCQ==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/resolve": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", @@ -3641,6 +3917,12 @@ "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz", "integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==" }, + "node_modules/@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", + "dev": true + }, "node_modules/@types/serve-index": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", @@ -3671,6 +3953,15 @@ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" }, + "node_modules/@types/testing-library__jest-dom": { + "version": "5.14.3", + "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.3.tgz", + "integrity": "sha512-oKZe+Mf4ioWlMuzVBaXQ9WDnEm1+umLx0InILg+yvZVBBDmzV5KfZyLrCvadtWcx8+916jLmHafcmqqffl+iIw==", + "dev": true, + "dependencies": { + "@types/jest": "*" + } + }, "node_modules/@types/trusted-types": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", @@ -4570,6 +4861,18 @@ "node": ">= 4.0.0" } }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true, + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, "node_modules/autoprefixer": { "version": "10.4.2", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.2.tgz", @@ -5628,6 +5931,17 @@ "node": ">=8" } }, + "node_modules/css": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", + "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.4", + "source-map": "^0.6.1", + "source-map-resolve": "^0.6.0" + } + }, "node_modules/css-blank-pseudo": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", @@ -5874,6 +6188,21 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=", + "dev": true + }, + "node_modules/css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/cssdb": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-6.4.0.tgz", @@ -5890,6 +6219,12 @@ "node": ">=4" } }, + "node_modules/cssfontparser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/cssfontparser/-/cssfontparser-1.2.1.tgz", + "integrity": "sha1-9AIvyPlwDGgCnVQghK+69CWj8+M=", + "dev": true + }, "node_modules/cssnano": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.0.tgz", @@ -6020,6 +6355,12 @@ "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" }, + "node_modules/csstype": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", + "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==", + "dev": true + }, "node_modules/customize-cra": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/customize-cra/-/customize-cra-1.0.0.tgz", @@ -6150,6 +6491,15 @@ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==" }, + "node_modules/decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, "node_modules/dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -6391,6 +6741,12 @@ "node": ">=6.0.0" } }, + "node_modules/dom-accessibility-api": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.13.tgz", + "integrity": "sha512-R305kwb5CcMDIpSHUnLyIAp7SrSPBx6F0VfQFB3M75xVMHhXJJIdePYgbPPh1o57vCHNu5QztokWUPsLjWzFqw==", + "dev": true + }, "node_modules/dom-converter": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", @@ -9202,6 +9558,16 @@ } } }, + "node_modules/jest-canvas-mock": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/jest-canvas-mock/-/jest-canvas-mock-2.3.1.tgz", + "integrity": "sha512-5FnSZPrX3Q2ZfsbYNE3wqKR3+XorN8qFzDzB5o0golWgt6EOX1+emBnpOc9IAQ+NXFj8Nzm3h7ZdE/9H0ylBcg==", + "dev": true, + "dependencies": { + "cssfontparser": "^1.2.1", + "moo-color": "^1.0.2" + } + }, "node_modules/jest-changed-files": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", @@ -11214,6 +11580,15 @@ "node": ">=10" } }, + "node_modules/lz-string": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", + "integrity": "sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=", + "dev": true, + "bin": { + "lz-string": "bin/bin.js" + } + }, "node_modules/magic-string": { "version": "0.25.7", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", @@ -11357,6 +11732,15 @@ "node": ">=6" } }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/mini-css-extract-plugin": { "version": "2.5.3", "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.5.3.tgz", @@ -11456,6 +11840,21 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/moo-color": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/moo-color/-/moo-color-1.0.2.tgz", + "integrity": "sha512-5iXz5n9LWQzx/C2WesGFfpE6RLamzdHwsn3KpfzShwbfIqs7stnoEpaNErf/7+3mbxwZ4s8Foq7I0tPxw7BWHg==", + "dev": true, + "dependencies": { + "color-name": "^1.1.4" + } + }, + "node_modules/moo-color/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -13902,6 +14301,19 @@ "node": "*" } }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -14614,6 +15026,17 @@ "webpack": "^5.0.0" } }, + "node_modules/source-map-resolve": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", + "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "dev": true, + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0" + } + }, "node_modules/source-map-support": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", @@ -14960,6 +15383,18 @@ "node": ">=6" } }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -19025,6 +19460,173 @@ "loader-utils": "^2.0.0" } }, + "@testing-library/dom": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.11.3.tgz", + "integrity": "sha512-9LId28I+lx70wUiZjLvi1DB/WT2zGOxUh46glrSNMaWVx849kKAluezVzZrXJfTKKoQTmEOutLes/bHg4Bj3aA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^4.2.0", + "aria-query": "^5.0.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.4.4", + "pretty-format": "^27.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "aria-query": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.0.0.tgz", + "integrity": "sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@testing-library/jest-dom": { + "version": "5.16.2", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.2.tgz", + "integrity": "sha512-6ewxs1MXWwsBFZXIk4nKKskWANelkdUehchEOokHsN8X7c2eKXGw+77aRV63UU8f/DTSVUPLaGxdrj4lN7D/ug==", + "dev": true, + "requires": { + "@babel/runtime": "^7.9.2", + "@types/testing-library__jest-dom": "^5.9.1", + "aria-query": "^5.0.0", + "chalk": "^3.0.0", + "css": "^3.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.5.6", + "lodash": "^4.17.15", + "redent": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "aria-query": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.0.0.tgz", + "integrity": "sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg==", + "dev": true + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@testing-library/react": { + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-12.1.4.tgz", + "integrity": "sha512-jiPKOm7vyUw311Hn/HlNQ9P8/lHNtArAx0PisXyFixDDvfl8DbD6EUdbshK5eqauvBSvzZd19itqQ9j3nferJA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.12.5", + "@testing-library/dom": "^8.0.0", + "@types/react-dom": "*" + } + }, + "@testing-library/user-event": { + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.5.0.tgz", + "integrity": "sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.12.5" + } + }, "@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -19035,6 +19637,12 @@ "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==" }, + "@types/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==", + "dev": true + }, "@types/babel__core": { "version": "7.1.18", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.18.tgz", @@ -19192,6 +19800,16 @@ "@types/istanbul-lib-report": "*" } }, + "@types/jest": { + "version": "27.4.1", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.4.1.tgz", + "integrity": "sha512-23iPJADSmicDVrWk+HT58LMJtzLAnB2AgIzplQuq/bSrGaxCrlvRFjGbXmamnnk/mAmCdLStiGqggu28ocUyiw==", + "dev": true, + "requires": { + "jest-matcher-utils": "^27.0.0", + "pretty-format": "^27.0.0" + } + }, "@types/json-schema": { "version": "7.0.9", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", @@ -19222,6 +19840,12 @@ "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.4.tgz", "integrity": "sha512-ReVR2rLTV1kvtlWFyuot+d1pkpG2Fw/XKE3PDAdj57rbM97ttSp9JZ2UsP+2EHTylra9cUf6JA7tGwW1INzUrA==" }, + "@types/prop-types": { + "version": "15.7.4", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz", + "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==", + "dev": true + }, "@types/q": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz", @@ -19237,6 +19861,26 @@ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" }, + "@types/react": { + "version": "17.0.40", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.40.tgz", + "integrity": "sha512-UrXhD/JyLH+W70nNSufXqMZNuUD2cXHu6UjCllC6pmOQgBX4SGXOH8fjRka0O0Ee0HrFxapDD8Bwn81Kmiz6jQ==", + "dev": true, + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@types/react-dom": { + "version": "17.0.13", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.13.tgz", + "integrity": "sha512-wEP+B8hzvy6ORDv1QBhcQia4j6ea4SFIBttHYpXKPFZRviBvknq0FRh3VrIxeXUmsPkwuXVZrVGG7KUVONmXCQ==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@types/resolve": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", @@ -19250,6 +19894,12 @@ "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz", "integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==" }, + "@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", + "dev": true + }, "@types/serve-index": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", @@ -19280,6 +19930,15 @@ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" }, + "@types/testing-library__jest-dom": { + "version": "5.14.3", + "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.3.tgz", + "integrity": "sha512-oKZe+Mf4ioWlMuzVBaXQ9WDnEm1+umLx0InILg+yvZVBBDmzV5KfZyLrCvadtWcx8+916jLmHafcmqqffl+iIw==", + "dev": true, + "requires": { + "@types/jest": "*" + } + }, "@types/trusted-types": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", @@ -19931,6 +20590,12 @@ "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, "autoprefixer": { "version": "10.4.2", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.2.tgz", @@ -20734,6 +21399,25 @@ "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" }, + "css": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", + "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==", + "dev": true, + "requires": { + "inherits": "^2.0.4", + "source-map": "^0.6.1", + "source-map-resolve": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, "css-blank-pseudo": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", @@ -20882,6 +21566,12 @@ "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz", "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==" }, + "css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=", + "dev": true + }, "cssdb": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-6.4.0.tgz", @@ -20892,6 +21582,12 @@ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" }, + "cssfontparser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/cssfontparser/-/cssfontparser-1.2.1.tgz", + "integrity": "sha1-9AIvyPlwDGgCnVQghK+69CWj8+M=", + "dev": true + }, "cssnano": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.0.tgz", @@ -20993,6 +21689,12 @@ } } }, + "csstype": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", + "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==", + "dev": true + }, "customize-cra": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/customize-cra/-/customize-cra-1.0.0.tgz", @@ -21119,6 +21821,12 @@ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==" }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, "dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -21310,6 +22018,12 @@ "esutils": "^2.0.2" } }, + "dom-accessibility-api": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.13.tgz", + "integrity": "sha512-R305kwb5CcMDIpSHUnLyIAp7SrSPBx6F0VfQFB3M75xVMHhXJJIdePYgbPPh1o57vCHNu5QztokWUPsLjWzFqw==", + "dev": true + }, "dom-converter": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", @@ -23299,6 +24013,16 @@ "jest-cli": "^27.5.1" } }, + "jest-canvas-mock": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/jest-canvas-mock/-/jest-canvas-mock-2.3.1.tgz", + "integrity": "sha512-5FnSZPrX3Q2ZfsbYNE3wqKR3+XorN8qFzDzB5o0golWgt6EOX1+emBnpOc9IAQ+NXFj8Nzm3h7ZdE/9H0ylBcg==", + "dev": true, + "requires": { + "cssfontparser": "^1.2.1", + "moo-color": "^1.0.2" + } + }, "jest-changed-files": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", @@ -24776,6 +25500,12 @@ "yallist": "^4.0.0" } }, + "lz-string": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", + "integrity": "sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=", + "dev": true + }, "magic-string": { "version": "0.25.7", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", @@ -24882,6 +25612,12 @@ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" }, + "min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true + }, "mini-css-extract-plugin": { "version": "2.5.3", "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.5.3.tgz", @@ -24953,6 +25689,23 @@ "minimist": "^1.2.5" } }, + "moo-color": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/moo-color/-/moo-color-1.0.2.tgz", + "integrity": "sha512-5iXz5n9LWQzx/C2WesGFfpE6RLamzdHwsn3KpfzShwbfIqs7stnoEpaNErf/7+3mbxwZ4s8Foq7I0tPxw7BWHg==", + "dev": true, + "requires": { + "color-name": "^1.1.4" + }, + "dependencies": { + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -26616,6 +27369,16 @@ } } }, + "redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "requires": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + } + }, "regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -27135,6 +27898,16 @@ "source-map-js": "^1.0.1" } }, + "source-map-resolve": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", + "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", + "dev": true, + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0" + } + }, "source-map-support": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", @@ -27418,6 +28191,15 @@ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" }, + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "requires": { + "min-indent": "^1.0.0" + } + }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", diff --git a/package.json b/package.json index 1aaf49e..6e48c4a 100644 --- a/package.json +++ b/package.json @@ -35,8 +35,12 @@ ] }, "devDependencies": { + "@testing-library/jest-dom": "^5.16.2", + "@testing-library/react": "^12.1.4", + "@testing-library/user-event": "^13.5.0", "autoprefixer": "^10.4.2", "customize-cra": "^1.0.0", + "jest-canvas-mock": "^2.3.1", "license-checker": "^25.0.1", "postcss": "^8.4.7", "react-app-rewired": "^2.2.1", diff --git a/src/main/react/App.js b/src/main/react/App.js index 99e4a8f..e401b4f 100644 --- a/src/main/react/App.js +++ b/src/main/react/App.js @@ -1,25 +1,12 @@ -import Header from "./Header"; -import Content from "./Content"; -import Footer from "./Footer"; +import {Routes, Route} from "react-router-dom"; +import Home from "./Home"; +import Dashboard from "./Dashboard"; export default function App() { return ( -
-
-
-
-
-
-

- Hit Counter for Your GitHub or Any Kind of Websites You Want -

-

Now it's time to hit's. Happy hacking !

-
-
- -
-
-
-
+ + }/> + }/> + ); } \ No newline at end of file diff --git a/src/main/react/App.test.js b/src/main/react/App.test.js new file mode 100644 index 0000000..6b084ac --- /dev/null +++ b/src/main/react/App.test.js @@ -0,0 +1,14 @@ +import {render} from "@testing-library/react"; +import {BrowserRouter, MemoryRouter} from "react-router-dom"; +import App from "./App"; + +describe("App", () => { + it("should render the Home", () => { + render(); + expect(document.body).toHaveTextContent("Hit Counter for Your GitHub or Any Kind of Websites You Want"); + }); + it("should render the Dashboard", () => { + render(); + expect(document.body).toHaveTextContent("github.com/silentsoft/hits"); + }); +}); \ No newline at end of file diff --git a/src/main/react/Content.test.js b/src/main/react/Content.test.js new file mode 100644 index 0000000..ec345bb --- /dev/null +++ b/src/main/react/Content.test.js @@ -0,0 +1,60 @@ +import {fireEvent, render, screen} from "@testing-library/react"; +import {BrowserRouter} from "react-router-dom"; +import Content from "./Content"; +import userEvent from "@testing-library/user-event"; + +test("Content", () => { + render(); + + const input = screen.getByPlaceholderText("https://github.com/silentsoft/hits"); + expect(input).toBeInTheDocument(); + + const viewType = screen.getByDisplayValue("total"); + expect(viewType).toBeInTheDocument(); + + const style = screen.getByDisplayValue("flat"); + expect(style).toBeInTheDocument(); + + const label = screen.getByPlaceholderText("hits"); + expect(label).toBeInTheDocument(); + + const extraCount = screen.getByPlaceholderText("0"); + expect(extraCount).toBeInTheDocument(); + + const color = screen.getByPlaceholderText("#4c1"); + expect(color).toBeInTheDocument(); + + const labelColor = screen.getByPlaceholderText("#555"); + expect(labelColor).toBeInTheDocument(); + + const logo = screen.getByPlaceholderText("slug or base64"); + expect(logo).toBeInTheDocument(); + + expect(screen.getAllByText("No URL specified")).toHaveLength(3); + + userEvent.type(input, "https://hits"); + expect(screen.getAllByText("Invalid URL")).toHaveLength(3); + + userEvent.clear(input); + userEvent.type(input, "https://hits.sh/"); + expect(screen.getByText(`[![Hits](https://hits.sh/hits.sh.svg)](https://hits.sh/hits.sh/)`)).toBeInTheDocument(); + expect(screen.getByText(`Hits`)).toBeInTheDocument(); + expect(screen.getByText(`https://hits.sh/hits.sh.svg`)).toBeInTheDocument(); + + userEvent.selectOptions(viewType, "today-total"); + userEvent.selectOptions(style, "flat-square"); + userEvent.type(label, "page views"); + + userEvent.type(extraCount, "1"); + fireEvent.keyPress(extraCount, {key: "a", code: "KeyA", charCode: "97", ctrlKey: true}); + fireEvent.keyPress(extraCount, {key: "a", code: "KeyA", charCode: "97"}); + fireEvent.keyPress(extraCount, {key: "0", code: "Digit0", charCode: "48"}); + userEvent.type(extraCount, "000"); + + userEvent.type(color, "#000"); + userEvent.type(labelColor, "#fff"); + userEvent.type(logo, "github"); + expect(screen.getByText(`[![Hits](https://hits.sh/hits.sh.svg?view=today-total&style=flat-square&label=page%20views&extraCount=1000&color=000&labelColor=fff&logo=github)](https://hits.sh/hits.sh/)`)).toBeInTheDocument(); + expect(screen.getByText(`Hits`)).toBeInTheDocument(); + expect(screen.getByText(`https://hits.sh/hits.sh.svg?view=today-total&style=flat-square&label=page%20views&extraCount=1000&color=000&labelColor=fff&logo=github`)).toBeInTheDocument(); +}); \ No newline at end of file diff --git a/src/main/react/Dashboard.js b/src/main/react/Dashboard.js index e5d3652..5e16821 100644 --- a/src/main/react/Dashboard.js +++ b/src/main/react/Dashboard.js @@ -48,15 +48,15 @@ export default function Dashboard() {
-

{weekly}

+

{weekly}

Weekly

-

{monthly}

+

{monthly}

Monthly

-

{total}

+

{total}

Total

@@ -66,7 +66,7 @@ export default function Dashboard() { {items.length > 0 ? ( items.reduce((acc, item, index) => { acc.push( -
+
+
{ + it("should render with items", async () => { + axios.get.mockResolvedValue({ + data: { + total: 3, + monthly: 2, + weekly: 1, + items: [ + { + from: "2021-01-01", + to: "2021-12-31", + data: [ + { + value: 1, + day: "2021-12-31", + }, + ], + }, + { + from: "2022-01-01", + to: "2022-12-31", + data: [ + { + value: 2, + day: "2022-01-01", + }, + ], + } + ] + } + }); + + await render(); + + expect(screen.getByText("github.com/silentsoft/hits")).toBeInTheDocument(); + expect(screen.getByTestId("weekly")).toHaveTextContent("1"); + expect(screen.getByText("Weekly")).toBeInTheDocument(); + expect(screen.getByTestId("monthly")).toHaveTextContent("2"); + expect(screen.getByText("Monthly")).toBeInTheDocument(); + expect(screen.getByTestId("total")).toHaveTextContent("3"); + expect(screen.getByText("Total")).toBeInTheDocument(); + expect(screen.getAllByTestId("chart")).toHaveLength(2); + }); + it("should render without items", () => { + axios.get.mockRejectedValue(new Error()); + + render(); + + expect(screen.getByText("github.com/silentsoft/hits")).toBeInTheDocument(); + expect(screen.queryByTestId("weekly")).not.toBeInTheDocument(); + expect(screen.queryByText("Weekly")).not.toBeInTheDocument(); + expect(screen.queryByTestId("monthly")).not.toBeInTheDocument(); + expect(screen.queryByText("Monthly")).not.toBeInTheDocument(); + expect(screen.queryByTestId("total")).not.toBeInTheDocument(); + expect(screen.queryByText("Total")).not.toBeInTheDocument(); + expect(screen.getAllByTestId("chart")).toHaveLength(1); + }); +}); \ No newline at end of file diff --git a/src/main/react/Footer.test.js b/src/main/react/Footer.test.js new file mode 100644 index 0000000..b868d91 --- /dev/null +++ b/src/main/react/Footer.test.js @@ -0,0 +1,9 @@ +import {render, screen} from "@testing-library/react"; +import Footer from "./Footer"; + +describe("Footer", () => { + it("should render the copyright notice", () => { + render(