diff --git a/.circleci/config.yml b/.circleci/config.yml
index d49a32f50..dd0d77bc6 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -63,6 +63,7 @@ build_configuration_fetch: &build_configuration_fetch
command: |
./awsconfiguration.sh $DEPLOY_ENV
./buildenv.sh -e $DEPLOY_ENV -b ${LOGICAL_ENV}-${APPNAME}-buildvar
+ aws s3 cp s3://tc-platform-${LOGICAL_ENV}/securitymanager/${LOGICAL_ENV}-platform-ui.env ./.env
lint_steps: &lint_steps
# Initialization.
@@ -146,6 +147,35 @@ jobs:
APPNAME: "platform-ui-mvp"
steps: *deploy_steps
+ # Test job for the cases when we don not need deployment.
+ e2e-test:
+ docker:
+ - image: cypress/browsers:node16.14.2-slim-chrome100-ff99-edge
+ steps:
+ - checkout
+ - restore_cache:
+ key: test-node-modules-{{ checksum "yarn.lock" }}
+ - run:
+ name: Config Git
+ command: git config --global url."https://git@".insteadOf git://
+ - run:
+ name: Install Dependencies
+ command: yarn install
+ no_output_timeout: 20m
+ - run:
+ name: Install Cypress Binary
+ command: yarn cypress install
+ - run:
+ name: Build the application
+ command: yarn build
+ no_output_timeout: 20m
+ - save_cache:
+ key: test-node-modules-{{ checksum "yarn.lock" }}
+ paths:
+ - node_modules
+ - /root/.cache/Cypress
+ - run: yarn cy:ci
+
workflows:
version: 2
build:
@@ -171,6 +201,9 @@ workflows:
ignore:
- master
+ - e2e-test:
+ context : org-global
+
- build-prod:
context : org-global
filters:
@@ -182,6 +215,7 @@ workflows:
context : org-global
requires:
- build-dev
+ - e2e-test
filters:
branches:
only:
diff --git a/.gitignore b/.gitignore
index 4d29575de..63c983693 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,7 @@
# testing
/coverage
+.nyc_output
# production
/build
@@ -17,7 +18,12 @@
.env.development.local
.env.test.local
.env.production.local
+.env
npm-debug.log*
yarn-debug.log*
yarn-error.log*
+
+# Editors
+.editorconfig
+.prettierrc
diff --git a/README.md b/README.md
index 4951a9a68..f3d7134a8 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,7 @@
The Platform UI is the official Topcoder web app to host all modern user interfaces to be used by all users.
-All future user interfaces at Topcoder will be implemented here.
-Pre-existing user interfaces will be ported to here over time until this is the only user interface any user sees when interacting with Topcoder.
+All future user interfaces at Topcoder will be implemented here. Pre-existing user interfaces will be ported to here over time until this is the only user interface any user sees when interacting with Topcoder.
>**NOTE:** The information in this file describes our coding standards and best practices. All new code should follow these guidelines both when coding new features as well as porting old features. Please take the time to read through this file in detail.
@@ -11,6 +10,7 @@ Pre-existing user interfaces will be ported to here over time until this is the
- [Local Environment Setup](#local-environment-setup)
- [Deployments](#deployments)
+- [Developer Center specific setup](#developer-center-contentful-api-key-and-space-id)
- [Yarn Commands](#yarn-commands)
# Application structure
@@ -81,29 +81,37 @@ You will need to add the following line to your hosts file. The hosts file is no
>% yarn start
-3. Go to https://local.topcoder-dev.com:3003/
+3. Go to https://local.topcoder-dev.com:3000
+
+>**NOTE**: The default port is 3000, but you can override it in your [personal config](#personal-config).
### Local SSL
SSL is required for authentication to work properly.
-The `yarn start` command serves the site using the cert and key in the /ssl directory.
+The `yarn start` command serves the site using the cert and key in the /ssl directory, which authorize the `https://local.topcoder-dev.com`URL.
+
+By overriding the app to use port 443, you can use the authorized URL and trust the root CA to avoid SSL errors in the browser.
+
+>**NOTE:** Mac users will require running the app with elevated permissions in order to use a port lower than 500.
-For easier development, it is recommended that you add this certificate to your trusted root authorities and as a trused cert in your browser. Google your browser and OS for more info.
+For easier development, it is recommended that you add this certificate to your trusted root authorities and as a trused cert in your browser. Google your browser and OS for more info on how to trust cert authorities.
Otherwise, you will need to override the exception each time you load the site. Firefox users may need to user an incognito browser in order to override the exception.
### Personal Config
-1. Add [hostname] to src-ts/config/environments/app-host-environment.enum.ts
-2. Copy an existing config from src-ts/config/environments/environment.*.config.ts
-3. Rename new config environment.[hostname].config.ts
-4. Rename config variable to EnvironmentConfig[HostName]
-5. Set the ENV variable to AppHostEnvironment.[hostname]
-6. Add the switch case for the host name to src-ts/config/environments/environment.config.ts
+1. Add [hostname] to [`/src-ts/config/environments/app-host-environment.type.ts`](/src-ts/config/environments/app-host-environment.type.ts)
+2. Copy an existing config from [`/src-ts/config/environments/environment.*.config.ts`](/src-ts/config/environments/environment.bsouza.config.ts)
+3. Rename new config `environment.[hostname].config.ts`
+4. Rename config variable to `EnvironmentConfig[HostName]`
+5. Set the `ENV` variable to `[hostname]`
+6. Add the switch case for the host name to [`/src-ts/config/environments/environment.config.ts`](/src-ts/config/environments/environment.config.ts)
7. Prior to starting the server, set your host name:
```% export REACT_APP_HOST_ENV=[hostname]```
+>**NOTE:** Individual tools (e.g. [Learn tool](/src-ts/tools/learn/README.md)) can have their own configuration, which can be configured the same way as the global config.
+
#### For further convenience
1. Copy start-ssl-*.sh
@@ -117,7 +125,22 @@ The app uses CircleCI for CI/CD.
The "dev" branch is auto-deployed to the dev environment: https://platform-mvp.topcoder-dev.com.
-The "master" branch is auto-deployed to the production environment: https://platform-mvp.topcoder.com.
+The "master" branch is auto-deployed to the production environment: https://platform-ui.topcoder.com.
+
+## Developer Center Contentful API Key and Space Id
+
+The app requires two environment variables, which contain the space id and the key used to access contentful and retrieve Thrive Articles.
+
+You should create a file named `.env` in the root folder, and write inside the following lines:
+
+```sh
+REACT_APP_CONTENTFUL_EDU_SPACE_ID=
+REACT_APP_CONTENTFUL_EDU_CDN_API_KEY=
+```
+
+We should use the same space ID and API Key as Topcoder Thrive, these are for fetching Thrive articles and videos in the landing page.
+
+See the [Dev Center README](/src-ts/tools/dev-center/README.md) for further instructions on setting up the Dev Center.
## yarn Commands
@@ -132,7 +155,11 @@ The "master" branch is auto-deployed to the production environment: https://plat
| `yarn eslint` | Run eslint against js/x files and outputs report |
| `yarn eslint:fix` | Run eslint against js/x files, fixes auto-fixable issues, and outputs report |
| `yarn test` | Run unit tests, watching for changes and re-running per your specifications |
-| `yarn test:no-watch` | Run unit tests once, without watching for changes or re-running |
+| `yarn test:no-watch` | Run unit tests once, without watching for changes or re-running |
+| `yarn cy:run` | Run e2e tests once in local command with the site is running |
+| `yarn cy:ci` | Run e2e tests once by circle ci |
+| `yarn report:coverage`| Generate e2e coverage report in html format |
+| `yarn report:coverage:text` | Generate e2e coverage report in text format |
## Folder Structure
@@ -247,9 +274,10 @@ The PlatformRoute model has several useful options:
| `element: JSX.Element` | The element property is the JSX element that should appear at the specified URL. |
| `disabled?: boolean` | When a route is marked as disabled, it will not be registered and will the URL will return a 404. |
| `hide?: boolean` | When a route is hidden, it will be registered and the URL will be available through deep-linking but will not be visible in either the Tools or Utils Selectors. This is useful for handling redirects for obsolete routes. |
-| `requireAuth?: boolean` | Requiring authentication for a route means that users who are not logged in will be redirected to the Login Form when they try to access the route. |
+| `authRequired?: boolean` | Requiring authentication for a route means that users who are not logged in will be redirected to the Login Form when they try to access the route. |
| `route: string` | The route property is the path to the route, relative to its parent(s). |
| `title: string` | The title property is the text that will appear in the Tools or Utils Selectors (this is irrelevant on hidden routes). |
+| `rolesRequired: Array` | Requiring roles for a route means that users who do not own the roles will be presented with restricted page when they try to access the route. |
## Git
@@ -465,8 +493,8 @@ e.g.:
```
.logo-link {
svg {
- width: calc($pad-xxl + $pad-xxxxl);
- height: $pad-xl;
+ width: calc($space-xxl + $space-xxxxl);
+ height: $space-xl;
fill: none;
path {
diff --git a/babel.config.js b/babel.config.js
index cd5b145c6..457f10d59 100644
--- a/babel.config.js
+++ b/babel.config.js
@@ -50,6 +50,7 @@ module.exports = function (api) {
],
],
plugins: [
+ "istanbul",
[
"module-resolver",
{
diff --git a/cypress.config.ts b/cypress.config.ts
new file mode 100644
index 000000000..e622ea360
--- /dev/null
+++ b/cypress.config.ts
@@ -0,0 +1,19 @@
+import { defineConfig } from 'cypress'
+
+export default defineConfig({
+ fixturesFolder: false,
+ video: false,
+ screenshotOnRunFailure: false,
+ defaultCommandTimeout: 10000,
+ e2e: {
+ baseUrl: 'http://localhost:3000',
+ specPattern: "cypress/e2e/**/*.spec.{js,jsx,ts,tsx}",
+ supportFile: "cypress/support/e2e.ts",
+ viewportHeight: 1000,
+ viewportWidth: 1280,
+ setupNodeEvents(on, config) {
+ require('@cypress/code-coverage/task')(on, config)
+ return config;
+ },
+ },
+})
diff --git a/cypress/.eslintrc b/cypress/.eslintrc
new file mode 100644
index 000000000..0f1b15145
--- /dev/null
+++ b/cypress/.eslintrc
@@ -0,0 +1,3 @@
+{
+ "extends": ["plugin:cypress/recommended"]
+}
diff --git a/cypress/e2e/home/home.spec.ts b/cypress/e2e/home/home.spec.ts
new file mode 100644
index 000000000..8d1f98c25
--- /dev/null
+++ b/cypress/e2e/home/home.spec.ts
@@ -0,0 +1,8 @@
+describe('Landing Page', () => {
+
+ beforeEach(() => cy.visit('/'))
+
+ it('loads landing page should be successfully', () => {
+ cy.get('[data-id="root"]').should('be.visible')
+ })
+})
diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts
new file mode 100644
index 000000000..7d3a21716
--- /dev/null
+++ b/cypress/support/commands.ts
@@ -0,0 +1,8 @@
+///
+
+Cypress.on('uncaught:exception', () => {
+ // returning false here prevents Cypress from failing the test
+ return false
+})
+
+export {}
\ No newline at end of file
diff --git a/cypress/support/e2e.ts b/cypress/support/e2e.ts
new file mode 100644
index 000000000..cdb3e6d83
--- /dev/null
+++ b/cypress/support/e2e.ts
@@ -0,0 +1,2 @@
+import '@cypress/code-coverage/support'
+import './commands'
\ No newline at end of file
diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json
new file mode 100644
index 000000000..2aa16b188
--- /dev/null
+++ b/cypress/tsconfig.json
@@ -0,0 +1,12 @@
+{
+ "extends": "../tsconfig.json",
+ "include": ["./**/*.ts"],
+ "exclude": [],
+ "compilerOptions": {
+ "types": ["cypress"],
+ "lib": ["es2015", "dom"],
+ "isolatedModules": false,
+ "allowJs": true,
+ "noEmit": true
+ }
+}
\ No newline at end of file
diff --git a/package.json b/package.json
index 1fa4fdc1f..bf9628928 100644
--- a/package.json
+++ b/package.json
@@ -3,8 +3,9 @@
"version": "0.1.0",
"private": true,
"scripts": {
+ "dev": "yarn react-app-rewired start",
"start": "sh start-ssl.sh",
- "start:bsouza": "sh start-ssl-bsouza.sh",
+ "start:bsouza": "sudo sh start-ssl-bsouza.sh",
"build": "yarn react-app-rewired build",
"lint": "tslint 'src-ts/**/*.{ts,tsx}' && eslint 'src*/**/*.{js,jsx,ts,tsx}'",
"lint:fix": "tslint 'src-ts/**/*.{ts,tsx}' --fix && eslint 'src*/**/*.{js,jsx,ts,tsx}' --fix",
@@ -13,19 +14,30 @@
"eslint": "eslint 'src/**/*.{js,jsx}'",
"eslint:fix": "eslint 'src/**/*.{js,jsx}' --fix",
"test": "react-scripts test --watchAll",
- "test:no-watch": "react-scripts test --watchAll=false --passWithNoTests"
+ "test:no-watch": "react-scripts test --watchAll=false --passWithNoTests",
+ "cy:run": "cypress run",
+ "cy:ci": "start-server-and-test 'serve -s build -n -p 3000' http://localhost:3000 'cy:run'",
+ "report:coverage": "nyc report --reporter=html",
+ "report:coverage:text": "nyc report --reporter=text"
},
"dependencies": {
"@datadog/browser-logs": "^4.7.1",
"@heroicons/react": "^1.0.6",
+ "@types/dompurify": "^2.3.3",
+ "@types/highlightjs": "^9.12.2",
+ "@types/marked": "4.0.3",
"apexcharts": "^3.35.3",
"axios": "^0.26.1",
"browser-cookies": "^1.2.0",
"classnames": "^2.3.1",
+ "contentful": "^9.1.33",
"crypto-js": "^4.1.1",
"customize-cra": "^1.0.0",
+ "dompurify": "^2.3.10",
+ "highlight.js": "^11.6.0",
"html2canvas": "^1.4.1",
"lodash": "^4.17.21",
+ "marked": "4.0.3",
"moment": "^2.29.3",
"moment-timezone": "^0.5.34",
"prop-types": "^15.8.1",
@@ -63,6 +75,7 @@
"@babel/preset-react": "^7.7.4",
"@babel/preset-typescript": "^7.16.7",
"@babel/runtime": "^7.8.7",
+ "@cypress/code-coverage": "^3.10.0",
"@stripe/react-stripe-js": "1.7.2",
"@stripe/stripe-js": "1.29.0",
"@testing-library/jest-dom": "^5.14.1",
@@ -71,7 +84,7 @@
"@types/axios": "^0.14.0",
"@types/jest": "^27.0.1",
"@types/lodash": "^4.14.182",
- "@types/node": "^17.0.24",
+ "@types/node": "^18.7.13",
"@types/reach__router": "^1.3.10",
"@types/react": "^18.0.5",
"@types/react-dom": "^18.0.1",
@@ -90,25 +103,32 @@
"concurrently": "^5.0.1",
"config": "^3.3.6",
"cross-env": "^7.0.2",
+ "cypress": "^10.6.0",
"eslint": "^8.18.0",
"eslint-config-prettier": "^6.7.0",
"eslint-config-react-app": "^7.0.1",
"eslint-config-react-important-stuff": "^2.0.0",
+ "eslint-plugin-cypress": "^2.12.1",
"eslint-plugin-prettier": "^3.1.1",
"file-loader": "^6.2.0",
"husky": "^8.0.0",
"identity-obj-proxy": "^3.0.0",
+ "istanbul-lib-coverage": "^3.2.0",
"jest": "^25.2.7",
"jest-cli": "^25.2.7",
"lint-staged": "^13.0.3",
+ "nyc": "^15.1.0",
"postcss-loader": "^4.0.4",
"postcss-scss": "^3.0.2",
"prettier": "^2.0.4",
"pretty-quick": "^2.0.1",
"resolve-url-loader": "^3.1.2",
"sass-loader": "^10.0.5",
+ "serve": "^14.0.1",
+ "start-server-and-test": "^1.14.0",
"style-loader": "^2.0.0",
"systemjs-webpack-interop": "^2.1.2",
+ "tslint": "^6.1.3",
"webpack": "^4.41.2",
"webpack-cli": "^3.3.10",
"webpack-config-single-spa-react": "^1.0.3",
diff --git a/public/index.html b/public/index.html
index 59b04f059..da3a59318 100644
--- a/public/index.html
+++ b/public/index.html
@@ -32,7 +32,7 @@
-
+
-