mirror of
https://github.com/fergalmoran/onearmy-community-platform.git
synced 2025-12-22 01:30:48 +00:00
chore: vite migration
This commit is contained in:
@@ -106,7 +106,7 @@ commands:
|
||||
- run:
|
||||
name: Set branch environment
|
||||
command: |
|
||||
echo 'export REACT_APP_PROJECT_VERSION=${CIRCLE_SHA1}' >> $BASH_ENV
|
||||
echo 'export VITE_APP_PROJECT_VERSION=${CIRCLE_SHA1}' >> $BASH_ENV
|
||||
- run:
|
||||
name: Inject target environment configuration
|
||||
command: |
|
||||
@@ -200,7 +200,6 @@ jobs:
|
||||
resource_class: large
|
||||
environment:
|
||||
CYPRESS_INSTALL_BINARY: 0
|
||||
JEST_JUNIT_OUTPUT_DIR: ./reports
|
||||
steps:
|
||||
- setup_repo
|
||||
- attach_workspace:
|
||||
@@ -249,8 +248,6 @@ jobs:
|
||||
- run:
|
||||
# NOTE - run-in-band to try reduce memory leaks (https://github.com/facebook/jest/issues/7874)
|
||||
command: yarn run test:unit && yarn run test:components
|
||||
environment:
|
||||
JEST_JUNIT_OUTPUT_DIR: reports
|
||||
- store_artifacts:
|
||||
path: coverage
|
||||
- store_artifacts:
|
||||
@@ -288,13 +285,13 @@ jobs:
|
||||
- run:
|
||||
name: Set branch environment
|
||||
command: |
|
||||
echo 'export REACT_APP_BRANCH=${CIRCLE_BRANCH}' >> $BASH_ENV
|
||||
echo 'export REACT_APP_PROJECT_VERSION=${CIRCLE_SHA1}' >> $BASH_ENV
|
||||
echo 'export VITE_APP_BRANCH=${CIRCLE_BRANCH}' >> $BASH_ENV
|
||||
echo 'export VITE_APP_PROJECT_VERSION=${CIRCLE_SHA1}' >> $BASH_ENV
|
||||
- run:
|
||||
name: Check environment variables
|
||||
command: |
|
||||
echo REACT_APP_BRANCH=$REACT_APP_BRANCH
|
||||
echo $REACT_APP_PROJECT_VERSION
|
||||
echo VITE_APP_BRANCH=$VITE_APP_BRANCH
|
||||
echo $VITE_APP_PROJECT_VERSION
|
||||
- run:
|
||||
command: yarn build
|
||||
- persist_to_workspace:
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
// Run a pre-flight check that developer environment setup in compatible way
|
||||
const { envCheck } = require('./scripts/envCheck')
|
||||
if (!process.env.CI) {
|
||||
envCheck()
|
||||
}
|
||||
|
||||
// Specific settings to use when running anything that requires a webpack compiler
|
||||
// Enabled when npm command specifies `env-cmd -e webpack`
|
||||
let webpack = {
|
||||
NODE_OPTIONS: getNodeOptions(),
|
||||
}
|
||||
|
||||
// Specific env to use with react-scripts / create-react-app
|
||||
// Enabled when npm command specifies `env-cmd -e cra`
|
||||
let cra = {
|
||||
...webpack,
|
||||
FAST_REFRESH: false,
|
||||
}
|
||||
|
||||
exports.cra = cra
|
||||
exports.webpack = webpack
|
||||
|
||||
/** Determine what node_options to provide depending on context */
|
||||
function getNodeOptions() {
|
||||
// Depending on node version use different environment variables to fix
|
||||
// specific build or run issues
|
||||
const NODE_VERSION = process.versions.node.split('.')[0]
|
||||
|
||||
let NODE_OPTIONS = process.env.NODE_OPTIONS || ''
|
||||
|
||||
// fix out-of-memory issues - default to 4GB but allow override from CI
|
||||
// NOTE - would like to auto-calculate but does not support CI (https://github.com/nodejs/node/issues/27170)
|
||||
if (!NODE_OPTIONS.includes('--max-old-space-size')) {
|
||||
NODE_OPTIONS += ` --max-old-space-size=4096`
|
||||
}
|
||||
if (NODE_VERSION > '17') {
|
||||
// fix https://github.com/facebook/create-react-app/issues/11708
|
||||
// https://github.com/facebook/create-react-app/issues/12431
|
||||
NODE_OPTIONS += ' --openssl-legacy-provider --no-experimental-fetch'
|
||||
}
|
||||
|
||||
if (process.env.CI) {
|
||||
console.log('NODE_OPTIONS', NODE_OPTIONS, '\n')
|
||||
}
|
||||
return NODE_OPTIONS.trim()
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
"import",
|
||||
"simple-import-sort",
|
||||
"sort-class-members",
|
||||
"jest"
|
||||
"vitest"
|
||||
],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
@@ -104,7 +104,6 @@
|
||||
"prefer": "type-imports"
|
||||
}
|
||||
],
|
||||
"jest/no-focused-tests": "error",
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"@typescript-eslint/interface-name-prefix": "off",
|
||||
@@ -133,7 +132,7 @@
|
||||
"camelCase": true,
|
||||
"pascalCase": true
|
||||
},
|
||||
"ignore": ["react-app-env.d.ts", "service-worker.ts"]
|
||||
"ignore": ["vite-env.d.ts", "service-worker.ts"]
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
|
||||
2
.github/workflows/docker-emulator-build.yml
vendored
2
.github/workflows/docker-emulator-build.yml
vendored
@@ -79,7 +79,7 @@ jobs:
|
||||
- name: Install npm dependencies
|
||||
run: yarn install --immutable
|
||||
- name: Set environment variables
|
||||
run: export REACT_APP_PROJECT_VERSION=${GITHUB_SHA}
|
||||
run: export VITE_APP_PROJECT_VERSION=${GITHUB_SHA}
|
||||
- name: Prepare Build
|
||||
run: yarn workspace oa-emulators-docker prepare
|
||||
# Populate the list of build args generated in the prepare script to local env as multi line string
|
||||
|
||||
6
.github/workflows/pr-preview.yml
vendored
6
.github/workflows/pr-preview.yml
vendored
@@ -45,9 +45,9 @@ jobs:
|
||||
- name: Install npm dependencies
|
||||
run: yarn install --immutable
|
||||
- name: Set environment variables
|
||||
run: export REACT_APP_PROJECT_VERSION=${GITHUB_SHA}
|
||||
run: export VITE_APP_PROJECT_VERSION=${GITHUB_SHA}
|
||||
- name: Check environment variables
|
||||
run: echo $REACT_APP_PROJECT_VERSION
|
||||
run: echo $VITE_APP_PROJECT_VERSION
|
||||
- name: Build for Preview
|
||||
run: npm run build
|
||||
env:
|
||||
@@ -55,7 +55,7 @@ jobs:
|
||||
# disable until fully resolved
|
||||
CI: false
|
||||
# specify the 'preview' site variant to populate the relevant firebase config
|
||||
REACT_APP_SITE_VARIANT: preview
|
||||
VITE_APP_SITE_VARIANT: preview
|
||||
# The hosting-deploy action calls firebase tools via npx, however installing globally
|
||||
# gives us control over what version will be made available
|
||||
- name: Install firebase-tools globally
|
||||
|
||||
@@ -10,6 +10,8 @@ nmMode: hardlinks-local
|
||||
|
||||
nodeLinker: node-modules
|
||||
|
||||
checksumBehavior: update
|
||||
|
||||
packageExtensions:
|
||||
"@storybook/core-common@*":
|
||||
dependencies:
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
/* global module */
|
||||
module.exports = {
|
||||
export default {
|
||||
extends: ['@commitlint/config-conventional'],
|
||||
/** Add optional custom formatter */
|
||||
// Temporarily removed due to unresolved error: https://app.circleci.com/pipelines/github/ONEARMY/community-platform/5891/workflows/59757a07-b416-43be-9a57-eedc1190d5a0/jobs/44654
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
import { DefinePlugin, ProvidePlugin } from 'webpack'
|
||||
|
||||
import type { JestConfigOverride, WebpackConfigOverride } from '@craco/types'
|
||||
import type { RuleSetRule } from 'webpack'
|
||||
|
||||
/**
|
||||
* Craco is used to provide config overrides to the default webpack config that is called
|
||||
* from react-scripts.
|
||||
*/
|
||||
module.exports = {
|
||||
webpack: {
|
||||
configure: (webpackConfig: WebpackConfigOverride['webpackConfig']) => {
|
||||
// Add polyfills for node (mostly imports for pino-logflare)
|
||||
// https://github.com/facebook/create-react-app/issues/11756
|
||||
// https://stackoverflow.com/questions/68707553/uncaught-referenceerror-buffer-is-not-defined
|
||||
webpackConfig.resolve!.fallback = {
|
||||
stream: require.resolve('stream-browserify'),
|
||||
buffer: require.resolve('buffer'),
|
||||
}
|
||||
webpackConfig.module!.rules = hackUpdateRulesToSupportCJS(
|
||||
webpackConfig.module!.rules as RuleSetRule[],
|
||||
)
|
||||
webpackConfig.plugins = [
|
||||
...(webpackConfig.plugins as any[]),
|
||||
// Fix calls to process (pino-logflare and cypress calling db.ts outside of cra)
|
||||
// NOTE - react creates 'process.env' variable but does not assign anything to 'process'
|
||||
// https://github.com/facebook/create-react-app/issues/11951
|
||||
new DefinePlugin({
|
||||
process: {},
|
||||
}),
|
||||
new ProvidePlugin({
|
||||
Buffer: ['buffer', 'Buffer'],
|
||||
}),
|
||||
]
|
||||
// Fix sourcemap warning
|
||||
// https://github.com/facebook/create-react-app/discussions/11767
|
||||
webpackConfig.ignoreWarnings = [
|
||||
function ignoreSourcemapsloaderWarnings(warning) {
|
||||
return (
|
||||
warning.module &&
|
||||
(warning.module as any).resource.includes('node_modules') &&
|
||||
warning.details &&
|
||||
warning.details.includes('source-map-loader')
|
||||
)
|
||||
},
|
||||
]
|
||||
return webpackConfig
|
||||
},
|
||||
},
|
||||
|
||||
jest: {
|
||||
configure: (jestConfig: JestConfigOverride['jestConfig']) => {
|
||||
// https://kulshekhar.github.io/ts-jest/docs/getting-started/paths-mapping/
|
||||
jestConfig.reporters = [
|
||||
[
|
||||
'jest-junit',
|
||||
{ outputDirectory: 'reports', outputName: 'report.xml' },
|
||||
],
|
||||
]
|
||||
|
||||
jestConfig.moduleNameMapper = {
|
||||
...jestConfig.moduleNameMapper,
|
||||
'photoswipe/lightbox':
|
||||
'<rootDir>/node_modules/photoswipe/dist/umd/photoswipe-lightbox.umd.min.js',
|
||||
'photoswipe/style.css':
|
||||
'<rootDir>/node_modules/photoswipe/dist/photoswipe.css',
|
||||
// Allow specific import from 'src' (used to import `useCommonStores`)
|
||||
'^src$': '<rootDir>/src/index',
|
||||
}
|
||||
|
||||
return jestConfig
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepend a custom rule to support CJS files
|
||||
*
|
||||
* NOTE - should be resolved in future CRA release pending merge of
|
||||
* https://github.com/facebook/create-react-app/pull/12021
|
||||
*/
|
||||
const hackUpdateRulesToSupportCJS = (rules: RuleSetRule[]) => {
|
||||
return rules.map((rule) => {
|
||||
if (rule.oneOf instanceof Array) {
|
||||
rule.oneOf[rule.oneOf.length - 1].exclude = [
|
||||
/\.(js|mjs|jsx|cjs|ts|tsx)$/,
|
||||
/\.html$/,
|
||||
/\.json$/,
|
||||
]
|
||||
}
|
||||
return rule
|
||||
})
|
||||
}
|
||||
64
index.html
Normal file
64
index.html
Normal file
@@ -0,0 +1,64 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, shrink-to-fit=no"
|
||||
/>
|
||||
<link rel="preconnect" href="https://storage.googleapis.com" crossorigin />
|
||||
<link
|
||||
rel="preconnect"
|
||||
href="https://firebasestorage.googleapis.com"
|
||||
crossorigin
|
||||
/>
|
||||
<noscript><link rel="stylesheet" href="path/to/stylesheet.css" /></noscript>
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta property="og:title" content="Community Platform" />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="A series of tools for the Precious Plastic community to collaborate around the world. Connect, share and meet each other to tackle plastic waste."
|
||||
/>
|
||||
<meta property="og:image" content="./social-image.jpg" />
|
||||
<meta property="og:url" content="https://community.preciousplastic.com" />
|
||||
<meta name="twitter:title" content="Community Platform" />
|
||||
<meta
|
||||
name="twitter:description"
|
||||
content="A series of tools for the Precious Plastic community to collaborate around the world. Connect, share and meet each other to tackle plastic waste."
|
||||
/>
|
||||
<meta name="twitter:image" content="./social-image.jpg" />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is added to the
|
||||
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="./manifest.json" />
|
||||
<link id="favicon" rel="shortcut icon" href="./favicon.ico" />
|
||||
<!--
|
||||
Notice the use of ./public in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
Unlike "/favicon.ico" or "favicon.ico", "./favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>Precious Plastic Community</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="A series of tools for the Precious Plastic community to collaborate around the world. Connect, share and meet each other to tackle plastic waste."
|
||||
/>
|
||||
<script id="CommunityPlatform">
|
||||
/**
|
||||
* The following object has environment specific
|
||||
* configuration injected into it as part of the build process
|
||||
*/
|
||||
window.__OA_COMMUNITY_PLATFORM_CONFIGURATION = {}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body style="overflow-x: hidden">
|
||||
<noscript> You need to enable JavaScript to run this app. </noscript>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/index.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,8 +0,0 @@
|
||||
import type { JestConfigWithTsJest } from 'ts-jest'
|
||||
|
||||
const config: JestConfigWithTsJest = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
}
|
||||
|
||||
export default config
|
||||
51
package.json
51
package.json
@@ -12,22 +12,22 @@
|
||||
],
|
||||
"private": true,
|
||||
"main": "lib/index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "concurrently --kill-others --names themes,components,platform --prefix-colors cyan,blue,magenta \"yarn start:themes\" \"yarn start:components\" \"yarn start:platform\"",
|
||||
"start:themes": "yarn workspace oa-themes dev",
|
||||
"start:components": "yarn workspace oa-components dev",
|
||||
"start:platform": "yarn build:shared && env-cmd -e cra yarn craco start",
|
||||
"start:platform": "yarn build:shared && vite",
|
||||
"start:emulated": "concurrently --kill-others --names functions,themes,components,platform --prefix-colors yellow,cyan,blue,magenta \"yarn workspace functions start\" \"yarn start:themes\" \"yarn start:components\" \"cross-env PORT=4000 yarn start:platform\"",
|
||||
"start:emulated:docker": "concurrently --names functions,themes,components,platform,emulators --prefix-colors yellow,cyan,blue,magenta,green --kill-others \"yarn workspace functions watch\" \"yarn start:themes\" \"yarn start:components\" \"cross-env PORT=4000 yarn start:platform\" \"yarn workspace oa-emulators-docker start\"",
|
||||
"start:emulated:docker:local": "concurrently --names functions,themes,components,platform,emulators --prefix-colors yellow,cyan,blue,magenta,green --kill-others \"yarn workspace functions watch\" \"yarn start:themes\" \"yarn start:components\" \"cross-env PORT=4000 yarn start:platform\" \"yarn workspace oa-emulators-docker start --repo=\"",
|
||||
"build:themes": "yarn workspace oa-themes build",
|
||||
"build:components": "yarn workspace oa-components build",
|
||||
"build:cra": "env-cmd -e cra craco build",
|
||||
"build:vite": "tsc && vite",
|
||||
"build:post": "yarn workspace oa-scripts post-cra-build",
|
||||
"build:inject-config": "yarn build:post",
|
||||
"build:shared": "yarn workspace oa-shared build",
|
||||
"build": "yarn build:themes && yarn build:components && yarn build:shared && yarn build:cra",
|
||||
"eject": "react-scripts eject",
|
||||
"build": "yarn build:themes && yarn build:components && yarn build:shared && yarn build:vite",
|
||||
"lint": "yarn lint:style && yarn lint:code",
|
||||
"lint:commits": " npx commitlint --from=$(git merge-base master HEAD) --verbose",
|
||||
"lint:code": "eslint . --ext .js,.jsx,.ts,.tsx src --color",
|
||||
@@ -39,7 +39,7 @@
|
||||
"serve": "npx serve -s build",
|
||||
"test": "yarn workspace oa-cypress start",
|
||||
"test:components": "yarn workspace oa-components test-ci",
|
||||
"test:unit": "yarn build:themes && yarn build:components && env-cmd -e cra craco test --env=jsdom --runInBand --logHeapUsage --coverage --reporters=default --reporters=jest-junit",
|
||||
"test:unit": "yarn build:themes && yarn build:components && vitest --logHeapUsage --coverage",
|
||||
"test:madge": "npx madge --circular --extensions ts,tsx ./ --exclude src/stores",
|
||||
"storybook": "yarn workspace oa-components start",
|
||||
"storybook:build": "yarn build:themes && yarn workspace oa-components build:sb",
|
||||
@@ -75,9 +75,13 @@
|
||||
"@emotion/react": "^11.10.6",
|
||||
"@emotion/styled": "^11.8.1",
|
||||
"@sentry/react": "^6.15.0",
|
||||
"@uppy/core": "^2.1.4",
|
||||
"@uppy/dashboard": "^2.1.3",
|
||||
"@uppy/react": "^2.1.2",
|
||||
"@uppy/compressor": "^1.1.4",
|
||||
"@uppy/core": "^3.11.3",
|
||||
"@uppy/dashboard": "^3.8.3",
|
||||
"@uppy/drag-drop": "^3.1.0",
|
||||
"@uppy/file-input": "^3.1.2",
|
||||
"@uppy/progress-bar": "^3.1.1",
|
||||
"@uppy/react": "^3.3.1",
|
||||
"countries-list": "^2.6.1",
|
||||
"date-fns": "^3.3.0",
|
||||
"debounce": "^1.2.0",
|
||||
@@ -99,6 +103,7 @@
|
||||
"oa-shared": "workspace:*",
|
||||
"oa-themes": "workspace:*",
|
||||
"react": "17.0.2",
|
||||
"react-country-flag": "^3.1.0",
|
||||
"react-dom": "17.0.2",
|
||||
"react-dropzone": "^10.1.10",
|
||||
"react-final-form": "6.5.3",
|
||||
@@ -110,8 +115,6 @@
|
||||
"react-leaflet-markercluster": "^2.0.0-rc3",
|
||||
"react-router": "^6.20.1",
|
||||
"react-router-dom": "^6.20.1",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-virtualized": "9.22.5",
|
||||
"rxjs": "^6.6.3",
|
||||
"theme-ui": "^0.15.7",
|
||||
"ts-node": "^10.4.0",
|
||||
@@ -119,20 +122,19 @@
|
||||
"yup": "^1.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^16.2.3",
|
||||
"@commitlint/config-conventional": "^16.2.1",
|
||||
"@commitlint/cz-commitlint": "^16.2.3",
|
||||
"@craco/craco": "^7.0.0",
|
||||
"@craco/types": "^7.0.0",
|
||||
"@commitlint/cli": "^19.3.0",
|
||||
"@commitlint/config-conventional": "^19.2.2",
|
||||
"@commitlint/cz-commitlint": "^19.2.0",
|
||||
"@emotion/babel-plugin": "^11.11.0",
|
||||
"@esbuild-plugins/node-globals-polyfill": "^0.2.3",
|
||||
"@faker-js/faker": "^8.4.1",
|
||||
"@semantic-release/changelog": "^6.0.1",
|
||||
"@semantic-release/git": "^10.0.1",
|
||||
"@testing-library/jest-dom": "^5.11.4",
|
||||
"@testing-library/jest-dom": "^6.4.5",
|
||||
"@testing-library/react": "^11.1.0",
|
||||
"@testing-library/user-event": "^14.4.3",
|
||||
"@types/browser-image-compression": "^1.0.9",
|
||||
"@types/flux-standard-action": "1.1.0",
|
||||
"@types/jest": "^27.4.1",
|
||||
"@types/node": "^20.8.0",
|
||||
"@types/pubsub-js": "^1.5.18",
|
||||
"@types/react": "17.0.2",
|
||||
@@ -141,10 +143,11 @@
|
||||
"@types/react-leaflet-markercluster": "^2.0.0",
|
||||
"@types/react-router-dom": "5.3.3",
|
||||
"@types/react-select": "^2.0.17",
|
||||
"@types/react-virtualized": "^9.21.10",
|
||||
"@types/styled-system": "^5.1.11",
|
||||
"@typescript-eslint/eslint-plugin": "^6.7.3",
|
||||
"@typescript-eslint/parser": "^6.7.3",
|
||||
"@vitejs/plugin-react": "^4.3.0",
|
||||
"@vitest/coverage-v8": "^1.6.0",
|
||||
"all-contributors-cli": "^6.20.0",
|
||||
"buffer": "^6.0.3",
|
||||
"chai-subset": "^1.6.0",
|
||||
@@ -158,16 +161,16 @@
|
||||
"eslint-import-resolver-typescript": "^3.6.1",
|
||||
"eslint-plugin-cypress": "^2.15.1",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-jest": "^27.4.2",
|
||||
"eslint-plugin-mocha": "^10.2.0",
|
||||
"eslint-plugin-prefer-arrow-functions": "^3.1.4",
|
||||
"eslint-plugin-prettier": "^5.0.0",
|
||||
"eslint-plugin-react": "^7.34.2",
|
||||
"eslint-plugin-simple-import-sort": "^10.0.0",
|
||||
"eslint-plugin-sort-class-members": "^1.18.0",
|
||||
"eslint-plugin-unicorn": "^48.0.1",
|
||||
"husky": "^7.0.4",
|
||||
"eslint-plugin-vitest": "^0.5.4",
|
||||
"husky": "^9.0.11",
|
||||
"idb": "^6.0.0",
|
||||
"jest-junit": "^16.0.0",
|
||||
"lint-staged": "^8.1.5",
|
||||
"mobx-react-devtools": "^6.0.3",
|
||||
"prettier": "2.5.1",
|
||||
@@ -176,9 +179,13 @@
|
||||
"start-server-and-test": "^1.11.0",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"terser": "3.14.1",
|
||||
"ts-jest": "^29.1.2",
|
||||
"ts-loader": "^7.0.5",
|
||||
"typescript": "^5.1.6",
|
||||
"vite": "^5.2.12",
|
||||
"vite-plugin-env-compatible": "^2.0.1",
|
||||
"vite-plugin-svgr": "^4.2.0",
|
||||
"vite-tsconfig-paths": "^4.3.2",
|
||||
"vitest": "^1.6.0",
|
||||
"wait-on": "^5.2.1",
|
||||
"workbox-background-sync": "^6.1.5",
|
||||
"workbox-broadcast-update": "^6.1.5",
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"private": true,
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"storybook": "yarn start",
|
||||
"start": "storybook dev -p 6006 --loglevel verbose",
|
||||
@@ -32,7 +33,7 @@
|
||||
"react-flag-icon-css": "^1.0.25",
|
||||
"react-icons": "^4.3.1",
|
||||
"react-image-crop": "^11.0.5",
|
||||
"react-player": "^2.12.0",
|
||||
"react-player": "^2.16.0",
|
||||
"react-portal": "^4.2.2",
|
||||
"react-router": "6.20.1",
|
||||
"react-router-dom": "^6.20.1",
|
||||
@@ -62,12 +63,13 @@
|
||||
"@types/mustache": "^4.1.2",
|
||||
"@types/react-flag-icon-css": "^1.0.5",
|
||||
"@types/react-portal": "^4.0.4",
|
||||
"@vitejs/plugin-react": "^3.1.0",
|
||||
"@vitest/coverage-c8": "^0.30.1",
|
||||
"@vitejs/plugin-react": "^4.3.0",
|
||||
"@vitest/coverage-v8": "^1.6.0",
|
||||
"babel-loader": "8.1.0",
|
||||
"eslint": "^8.50.0",
|
||||
"eslint-plugin-import": "^2.28.1",
|
||||
"eslint-plugin-storybook": "^0.6.13",
|
||||
"eslint-plugin-vitest": "^0.5.4",
|
||||
"jsdom": "^21.1.1",
|
||||
"mustache": "^4.2.0",
|
||||
"prettier": "2.5.1",
|
||||
@@ -75,6 +77,6 @@
|
||||
"react-dom": "^17.0.2",
|
||||
"ts-node": "^10.7.0",
|
||||
"typescript": "^5.1.6",
|
||||
"vitest": "^0.30.1"
|
||||
"vitest": "^1.6.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { render } from '../tests/utils'
|
||||
import { Default, NoCategory } from './Breadcrumbs.stories'
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { render } from '../tests/utils'
|
||||
import {
|
||||
Default,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { fireEvent } from '@testing-library/react'
|
||||
import { vi } from 'vitest'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { render } from '../tests/utils'
|
||||
import { createFakeComments, fakeComment } from '../utils'
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { render } from '../tests/utils'
|
||||
import { Default } from './ContentStatistics.stories'
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
import { fireEvent } from '@testing-library/react'
|
||||
import { vi } from 'vitest'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { render } from '../tests/utils'
|
||||
import { CreateComment } from './CreateComment'
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
import { fireEvent, waitFor } from '@testing-library/react'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { render } from '../tests/utils'
|
||||
import { Default, LoggedIn, LoggedInWithError } from './CreateReply.stories'
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
import { act } from 'react-dom/test-utils'
|
||||
import { fireEvent } from '@testing-library/react'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { render } from '../tests/utils'
|
||||
import { Default, WithReplies } from './DiscussionContainer.stories'
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { render } from '../tests/utils'
|
||||
import { COMMENTS, NO_COMMENTS, ONE_COMMENT } from './DiscussionTitle'
|
||||
import {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { fireEvent } from '@testing-library/react'
|
||||
import { vi } from 'vitest'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { render } from '../tests/utils'
|
||||
import { DonationRequest, REQUEST_BUTTON_SKIP } from './DonationRequest'
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { vi } from 'vitest'
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { render } from '../tests/utils'
|
||||
import { DonationRequestModal } from './DonationRequestModal'
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { render } from '../tests/utils'
|
||||
import { Default } from './DownloadButton.stories'
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { render } from '../tests/utils'
|
||||
import { Default, One, Zero } from './DownloadCounter.stories'
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { render } from '../tests/utils'
|
||||
import { Default } from './DownloadFileFromLink.stories'
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
import { act, fireEvent } from '@testing-library/react'
|
||||
import { vi } from 'vitest'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { render } from '../tests/utils'
|
||||
import { EditComment, type IProps } from './EditComment'
|
||||
|
||||
@@ -1,30 +1,16 @@
|
||||
import * as React from 'react'
|
||||
import { ReactCountryFlag } from 'react-country-flag'
|
||||
import styled from '@emotion/styled'
|
||||
import { Box } from 'theme-ui'
|
||||
|
||||
import FlagIconFactory from 'react-flag-icon-css'
|
||||
|
||||
// Please only use `FlagIconFactory` one time in your application, there is no
|
||||
// need to use it multiple times (it would slow down your app). You may place the
|
||||
// line below in a `FlagIcon.js` file in your 'components' directory, then
|
||||
// write `export default FlagIcon` as shown below and import it elsewhere in your app.
|
||||
export const FlagIconFact = FlagIconFactory(React, { useCssModules: false })
|
||||
|
||||
export const FlagIconEvents = styled(FlagIconFact)`
|
||||
export const FlagIconEvents = styled(ReactCountryFlag)`
|
||||
border-radius: 5px;
|
||||
background-size: cover !important;
|
||||
height: 23px;
|
||||
width: 35px !important;
|
||||
`
|
||||
/*
|
||||
|
||||
@media only screen and (max-width: ${(props) => props.theme.breakpoints[1]}) {
|
||||
height: 15px;
|
||||
width: 25px !important;
|
||||
}
|
||||
*/
|
||||
|
||||
export const FlagIconHowTos = styled(FlagIconFact)`
|
||||
export const FlagIconHowTos = styled(ReactCountryFlag)`
|
||||
border-radius: 3px;
|
||||
background-size: cover !important;
|
||||
height: 14px;
|
||||
@@ -33,6 +19,8 @@ export const FlagIconHowTos = styled(FlagIconFact)`
|
||||
|
||||
export const FlagIcon = (props: any) => (
|
||||
<Box {...(props as any)}>
|
||||
<FlagIconEvents code={props.code}>{props.children}</FlagIconEvents>
|
||||
<FlagIconEvents countryCode={props.code} title={props.code} svg={true}>
|
||||
{props.children}
|
||||
</FlagIconEvents>
|
||||
</Box>
|
||||
)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { render } from '../tests/utils'
|
||||
import { Default } from './IconCountWithTooltip.stories'
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
import { fireEvent } from '@testing-library/react'
|
||||
import { vi } from 'vitest'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { render } from '../tests/utils'
|
||||
import { ImageCrop } from './ImageCrop'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// An edited version of https://codesandbox.io/p/sandbox/react-image-crop-demo-with-react-hooks-y831o
|
||||
|
||||
import React, { useRef, useState } from 'react'
|
||||
import ReactCrop from 'react-image-crop'
|
||||
import { ReactCrop } from 'react-image-crop'
|
||||
import { Box, Flex, Text } from 'theme-ui'
|
||||
|
||||
import { Button } from '../Button/Button'
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import '@testing-library/jest-dom/vitest'
|
||||
|
||||
import { findByRole as globalFindByRole, waitFor } from '@testing-library/react'
|
||||
import { vi } from 'vitest'
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { render } from '../tests/utils'
|
||||
import { ImageGallery } from './ImageGallery'
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { render } from '../tests/utils'
|
||||
import { MapMemberCard } from './MapMemberCard'
|
||||
import { Default, ModerationComments } from './MapMemberCard.stories'
|
||||
|
||||
@@ -3,14 +3,9 @@ import { Box, Flex } from 'theme-ui'
|
||||
|
||||
import { Icon } from '../Icon/Icon'
|
||||
|
||||
import type { NotificationType } from 'oa-shared'
|
||||
import type { NotificationType, UserNotificationItem } from 'oa-shared'
|
||||
import type { availableGlyphs } from '../Icon/types'
|
||||
|
||||
export interface UserNotificationItem {
|
||||
type: NotificationType
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
function getIconByType(type: NotificationType): availableGlyphs {
|
||||
if (['howto_useful', 'research_useful'].includes(type)) return 'useful'
|
||||
if (
|
||||
|
||||
@@ -4,7 +4,7 @@ import { InternalLink } from '../InternalLink/InternalLink'
|
||||
import { NotificationList } from './NotificationList'
|
||||
|
||||
import type { Meta, StoryFn } from '@storybook/react'
|
||||
import type { UserNotificationItem } from '../NotificationItem/NotificationItem'
|
||||
import type { UserNotificationItem } from 'oa-shared'
|
||||
|
||||
export default {
|
||||
title: 'Components/NotificationList',
|
||||
|
||||
@@ -5,8 +5,8 @@ import { Box, Card, Text } from 'theme-ui'
|
||||
import { Button } from '../Button/Button'
|
||||
import { NotificationItem } from '../NotificationItem/NotificationItem'
|
||||
|
||||
import type { UserNotificationItem } from 'oa-shared'
|
||||
import type { ThemeUIStyleObject } from 'theme-ui'
|
||||
import type { UserNotificationItem } from '../NotificationItem/NotificationItem'
|
||||
|
||||
export interface Props {
|
||||
notifications: UserNotificationItem[]
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { render } from '../tests/utils'
|
||||
import { Facebook, Instagram, Twitter, Youtube } from './ProfileLink.stories'
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { render } from '../tests/utils'
|
||||
import {
|
||||
Default,
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
import { act } from '@testing-library/react'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { render } from '../tests/utils'
|
||||
import { Default } from './TabbedContent.stories'
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { render } from '../tests/utils'
|
||||
import { TagList } from './TagList'
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { render } from '../tests/utils'
|
||||
import { Default } from './UserEngagementWrapper.stories'
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import '@testing-library/jest-dom/vitest'
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { render } from '../tests/utils'
|
||||
import { Default } from './UserStatistics.stories'
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { render } from '../tests/utils'
|
||||
import { Username } from './Username'
|
||||
import {
|
||||
|
||||
@@ -59,7 +59,11 @@ export const Username = ({ user, sx }: IProps) => {
|
||||
<Flex mr={1} sx={{ display: 'inline-flex' }}>
|
||||
{countryCode && isValidCountryCode(countryCode) ? (
|
||||
<Flex data-testid="Username: known flag">
|
||||
<FlagIconHowTos code={countryCode.toLowerCase()} />
|
||||
<FlagIconHowTos
|
||||
countryCode={countryCode}
|
||||
svg={true}
|
||||
title={countryCode}
|
||||
/>
|
||||
</Flex>
|
||||
) : (
|
||||
<Flex
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { render } from '../tests/utils'
|
||||
import { Youtube } from './VideoPlayer.stories'
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ReactPlayer from 'react-player/lazy'
|
||||
import ReactPlayer from 'react-player'
|
||||
import { Box } from 'theme-ui'
|
||||
|
||||
export interface Props {
|
||||
|
||||
@@ -57,4 +57,3 @@ export { UserEngagementWrapper } from './UserEngagementWrapper/UserEngagementWra
|
||||
export { IconCountWithTooltip } from './IconCountWithTooltip/IconCountWithTooltip'
|
||||
export { DonationRequest } from './DonationRequest/DonationRequest'
|
||||
export { DonationRequestModal } from './DonationRequestModal/DonationRequestModal'
|
||||
export { UserNotificationItem } from './NotificationItem/NotificationItem'
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import react from '@vitejs/plugin-react'
|
||||
import { defineConfig } from 'vite'
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
test: {
|
||||
environment: 'jsdom',
|
||||
globals: true,
|
||||
setupFiles: './src/tests/setup.ts',
|
||||
coverage: {
|
||||
provider: 'c8',
|
||||
reporter: ['text', 'json', 'html'],
|
||||
},
|
||||
},
|
||||
})
|
||||
24
packages/components/vite.config.ts
Normal file
24
packages/components/vite.config.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import react from '@vitejs/plugin-react'
|
||||
/// <reference types="vitest" />
|
||||
import { defineConfig } from 'vite'
|
||||
import svgr from 'vite-plugin-svgr'
|
||||
|
||||
import type { UserConfig as VitestUserConfigInterface } from 'vitest/config'
|
||||
|
||||
const vitestConfig: VitestUserConfigInterface = {
|
||||
test: {
|
||||
environment: 'jsdom',
|
||||
globals: true,
|
||||
setupFiles: './src/test/setup.ts',
|
||||
coverage: {
|
||||
provider: 'v8',
|
||||
},
|
||||
reporters: ['junit'],
|
||||
include: ['./src/**/*.{test,spec}.?(c|m)[jt]s?(x)'],
|
||||
},
|
||||
}
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default defineConfig({
|
||||
plugins: [react(), svgr()],
|
||||
test: vitestConfig.test,
|
||||
})
|
||||
@@ -54,7 +54,7 @@ async function main() {
|
||||
async function startAppServer() {
|
||||
const { CROSSENV_BIN, BUILD_SERVE_JSON } = PATHS
|
||||
// by default spawns will not respect colours used in stdio, so try to force
|
||||
const crossEnvArgs = `FORCE_COLOR=1 REACT_APP_SITE_VARIANT=test-ci`
|
||||
const crossEnvArgs = `FORCE_COLOR=1 VITE_APP_SITE_VARIANT=test-ci`
|
||||
|
||||
// run local debug server for testing unless production build specified
|
||||
let serverCmd = `${CROSSENV_BIN} ${crossEnvArgs} BROWSER=none PORT=3456 yarn start`
|
||||
|
||||
@@ -11,7 +11,7 @@ const e = process.env || ({} as any)
|
||||
* e.g. oa_
|
||||
* SessionStorage prefixes are used to allow test ci environments to dynamically set a db endpoint
|
||||
*/
|
||||
const DB_PREFIX = sessionStorage.DB_PREFIX || e.REACT_APP_DB_PREFIX || ''
|
||||
const DB_PREFIX = sessionStorage.DB_PREFIX || e.VITE_APP_DB_PREFIX || ''
|
||||
|
||||
/**
|
||||
* Mapping of generic database endpoints to specific prefixed and revisioned versions for the
|
||||
|
||||
@@ -44,29 +44,29 @@ See [circleci slack orb](https://github.com/CircleCI-Public/slack-orb) for info)
|
||||
|
||||
### Runtime Variables
|
||||
|
||||
Any variables prefixed with `REACT_APP_` are automatically included with the runtime build. Currently we require:
|
||||
Any variables prefixed with `VITE_APP_` are automatically included with the runtime build. Currently we require:
|
||||
|
||||
Firebase configuration
|
||||
|
||||
```
|
||||
REACT_APP_FIREBASE_API_KEY
|
||||
REACT_APP_FIREBASE_AUTH_DOMAIN
|
||||
REACT_APP_FIREBASE_DATABASE_URL
|
||||
REACT_APP_FIREBASE_MESSAGING_SENDER_ID
|
||||
REACT_APP_FIREBASE_PROJECT_ID
|
||||
REACT_APP_FIREBASE_STORAGE_BUCKET
|
||||
VITE_APP_FIREBASE_API_KEY
|
||||
VITE_APP_FIREBASE_AUTH_DOMAIN
|
||||
VITE_APP_FIREBASE_DATABASE_URL
|
||||
VITE_APP_FIREBASE_MESSAGING_SENDER_ID
|
||||
VITE_APP_FIREBASE_PROJECT_ID
|
||||
VITE_APP_FIREBASE_STORAGE_BUCKET
|
||||
```
|
||||
|
||||
Sentry error tracking
|
||||
|
||||
```
|
||||
REACT_APP_SENTRY_DSN
|
||||
VITE_APP_SENTRY_DSN
|
||||
```
|
||||
|
||||
Google Analytics
|
||||
|
||||
```
|
||||
REACT_APP_GA_TRACKING_ID
|
||||
VITE_APP_GA_TRACKING_ID
|
||||
```
|
||||
|
||||
### Misc Variables
|
||||
|
||||
@@ -38,17 +38,17 @@ You will need to set up a CircleCI context for each target environment. This con
|
||||
|
||||
- `FIREBASE_TOKEN`
|
||||
- `GOOGLE_APPLICATION_CREDENTIALS_JSON`
|
||||
- `REACT_APP_BRANCH`
|
||||
- `REACT_APP_FIREBASE_API_KEY`
|
||||
- `REACT_APP_FIREBASE_AUTH_DOMAIN`
|
||||
- `REACT_APP_FIREBASE_DATABASE_URL`
|
||||
- `REACT_APP_FIREBASE_MESSAGING_SENDER_ID`
|
||||
- `REACT_APP_FIREBASE_PROJECT_ID`
|
||||
- `REACT_APP_FIREBASE_STORAGE_BUCKET`
|
||||
- `REACT_APP_GA_TRACKING_ID`
|
||||
- `REACT_APP_PLATFORM_THEME`
|
||||
- `REACT_APP_CDN_URL` - `https://cdn-url.com` - this is the URL to the CDN where the assets are stored. This is used to load the assets from the CDN instead of the local server. It should **not** include a trailing slash.
|
||||
- `REACT_APP_PLATFORM_PROFILES` - comma separated list of available profiles. Use `ProfileType` from modules/profile/index for guidance here. For example: `member,workspace`
|
||||
- `REACT_APP_SUPPORTED_MODULES` – comma separated list of available modules. See `/src/modules/index.ts` for the definitions.
|
||||
- `REACT_APP_API_URL` – 'https://api-url.com' - this is the URL to the API service. It should **not** include a trailing slash.
|
||||
- `VITE_APP_BRANCH`
|
||||
- `VITE_APP_FIREBASE_API_KEY`
|
||||
- `VITE_APP_FIREBASE_AUTH_DOMAIN`
|
||||
- `VITE_APP_FIREBASE_DATABASE_URL`
|
||||
- `VITE_APP_FIREBASE_MESSAGING_SENDER_ID`
|
||||
- `VITE_APP_FIREBASE_PROJECT_ID`
|
||||
- `VITE_APP_FIREBASE_STORAGE_BUCKET`
|
||||
- `VITE_APP_GA_TRACKING_ID`
|
||||
- `VITE_APP_PLATFORM_THEME`
|
||||
- `VITE_APP_CDN_URL` - `https://cdn-url.com` - this is the URL to the CDN where the assets are stored. This is used to load the assets from the CDN instead of the local server. It should **not** include a trailing slash.
|
||||
- `VITE_APP_PLATFORM_PROFILES` - comma separated list of available profiles. Use `ProfileType` from modules/profile/index for guidance here. For example: `member,workspace`
|
||||
- `VITE_APP_SUPPORTED_MODULES` – comma separated list of available modules. See `/src/modules/index.ts` for the definitions.
|
||||
- `VITE_APP_API_URL` – 'https://api-url.com' - this is the URL to the API service. It should **not** include a trailing slash.
|
||||
- `SITE_NAME`
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
require('dotenv').config()
|
||||
import dotenv from 'dotenv'
|
||||
|
||||
dotenv.config()
|
||||
|
||||
import * as testing from '@firebase/rules-unit-testing'
|
||||
import { doc, getDoc, setDoc, setLogLevel } from 'firebase/firestore'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "CommonJS",
|
||||
"module": "ESNext",
|
||||
"lib": ["ESNext"],
|
||||
"types": ["node"],
|
||||
"moduleResolution": "Node",
|
||||
|
||||
@@ -55,7 +55,7 @@ const storage =
|
||||
* e.g. oa_
|
||||
* SessionStorage prefixes are used to allow test ci environments to dynamically set a db endpoint
|
||||
*/
|
||||
const DB_PREFIX = storage.DB_PREFIX || e.REACT_APP_DB_PREFIX || ''
|
||||
const DB_PREFIX = storage.DB_PREFIX || e.VITE_APP_DB_PREFIX || ''
|
||||
|
||||
/**
|
||||
* Mapping of generic database endpoints to specific prefixed and revisioned versions for the
|
||||
|
||||
@@ -23,3 +23,8 @@ export const NotificationTypes = [
|
||||
] as const
|
||||
|
||||
export type NotificationType = typeof NotificationTypes[number]
|
||||
|
||||
export type UserNotificationItem = {
|
||||
type: NotificationType
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { render } from '@testing-library/react'
|
||||
import { UserRole } from 'oa-shared'
|
||||
import { FactoryUser } from 'src/test/factories/User'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { AuthWrapper } from './AuthWrapper' // adjust this import according to your file structure
|
||||
|
||||
@@ -10,7 +11,7 @@ const mockUser = FactoryUser({
|
||||
userRoles: [UserRole.ADMIN],
|
||||
})
|
||||
|
||||
jest.mock('src/common/hooks/useCommonStores', () => ({
|
||||
vi.mock('src/common/hooks/useCommonStores', () => ({
|
||||
__esModule: true,
|
||||
useCommonStores: () => ({
|
||||
stores: {
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import '@testing-library/jest-dom/vitest'
|
||||
|
||||
import { ThemeProvider } from '@emotion/react'
|
||||
import { act, render, waitFor } from '@testing-library/react'
|
||||
import { Provider } from 'mobx-react'
|
||||
import { FactoryUser } from 'src/test/factories/User'
|
||||
import { testingThemeStyles } from 'src/test/utils/themeUtils'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { DiscussionWrapper } from './DiscussionWrapper'
|
||||
|
||||
@@ -11,12 +14,12 @@ import type { IDiscussion } from 'src/models'
|
||||
const Theme = testingThemeStyles
|
||||
const mockUser = FactoryUser()
|
||||
|
||||
jest.mock('src/common/hooks/useCommonStores', () => ({
|
||||
vi.mock('src/common/hooks/useCommonStores', () => ({
|
||||
__esModule: true,
|
||||
useCommonStores: () => ({
|
||||
stores: {
|
||||
discussionStore: {
|
||||
fetchOrCreateDiscussionBySource: () => jest.fn(),
|
||||
fetchOrCreateDiscussionBySource: () => vi.fn(),
|
||||
activeUser: () => mockUser,
|
||||
},
|
||||
},
|
||||
@@ -29,7 +32,7 @@ describe('DiscussionWrapper', () => {
|
||||
const discussionProps = {
|
||||
sourceType: 'question' as IDiscussion['sourceType'],
|
||||
sourceId: '82364tdf',
|
||||
setTotalCommentsCount: () => jest.fn(),
|
||||
setTotalCommentsCount: () => vi.fn(),
|
||||
}
|
||||
let wrapper
|
||||
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
import '@testing-library/jest-dom/vitest'
|
||||
|
||||
import { fireEvent, render } from '@testing-library/react'
|
||||
import { UserRole } from 'oa-shared'
|
||||
import { FactoryUser } from 'src/test/factories/User'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { useCommonStores } from './hooks/useCommonStores'
|
||||
import { DownloadWithDonationAsk } from './DownloadWithDonationAsk'
|
||||
|
||||
const mockedUsedNavigate = jest.fn()
|
||||
jest.mock('react-router-dom', () => ({
|
||||
const mockedUsedNavigate = vi.fn()
|
||||
vi.mock('react-router-dom', () => ({
|
||||
useNavigate: () => mockedUsedNavigate,
|
||||
}))
|
||||
|
||||
jest.mock('src/common/hooks/useCommonStores', () => ({
|
||||
vi.mock('src/common/hooks/useCommonStores', () => ({
|
||||
__esModule: true,
|
||||
useCommonStores: jest.fn(),
|
||||
useCommonStores: vi.fn(),
|
||||
}))
|
||||
const userToMock = (user) => {
|
||||
return useCommonStores.mockImplementation(() => ({
|
||||
@@ -24,7 +27,7 @@ describe('DownloadFileFromLink', () => {
|
||||
it('when logged out, requires users to login', () => {
|
||||
const { getAllByTestId } = render(
|
||||
<DownloadWithDonationAsk
|
||||
handleClick={jest.fn()}
|
||||
handleClick={vi.fn()}
|
||||
isLoggedIn={false}
|
||||
link="http://youtube.com/"
|
||||
/>,
|
||||
@@ -40,7 +43,7 @@ describe('DownloadFileFromLink', () => {
|
||||
const user = FactoryUser()
|
||||
userToMock(user)
|
||||
|
||||
const handleClick = jest.fn()
|
||||
const handleClick = vi.fn()
|
||||
const { getAllByTestId } = render(
|
||||
<DownloadWithDonationAsk
|
||||
handleClick={handleClick}
|
||||
@@ -59,7 +62,7 @@ describe('DownloadFileFromLink', () => {
|
||||
const user = FactoryUser({ userRoles: [UserRole.BETA_TESTER] })
|
||||
userToMock(user)
|
||||
|
||||
const handleClick = jest.fn()
|
||||
const handleClick = vi.fn()
|
||||
|
||||
const { getAllByTestId } = render(
|
||||
<DownloadWithDonationAsk
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { ErrorsContainer } from './ErrorsContainer'
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as React from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import Compressor from '@uppy/compressor'
|
||||
import Uppy from '@uppy/core'
|
||||
import { DashboardModal } from '@uppy/react'
|
||||
import { Button, DownloadStaticFile } from 'oa-components'
|
||||
@@ -24,8 +25,10 @@ interface IState {
|
||||
}
|
||||
export const FileInput = (props: IProps) => {
|
||||
const [state, setState] = useState<IState>({ open: false })
|
||||
const [uppy] = useState(
|
||||
() => new Uppy({ ...UPPY_CONFIG, onBeforeUpload: () => uploadTriggered() }),
|
||||
const [uppy] = useState(() =>
|
||||
new Uppy({ ...UPPY_CONFIG, onBeforeUpload: () => uploadTriggered() }).use(
|
||||
Compressor,
|
||||
),
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import '@testing-library/jest-dom/vitest'
|
||||
|
||||
import { fireEvent, render } from '@testing-library/react'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { HideDiscussionContainer } from './HideDiscussionContainer'
|
||||
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
import { act } from 'react-dom/test-utils'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import { render } from '@testing-library/react'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { ScrollToTop } from './ScrollToTop'
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
...jest.requireActual('react-router-dom'),
|
||||
useLocation: jest.fn(),
|
||||
import type { Mock } from 'vitest'
|
||||
|
||||
vi.mock('react-router-dom', async () => ({
|
||||
...(await vi.importActual('react-router-dom')),
|
||||
useLocation: vi.fn(),
|
||||
}))
|
||||
|
||||
describe('ScrollToTop', () => {
|
||||
it('should scroll to top when pathname changes', async () => {
|
||||
const scrollToSpy = jest.fn()
|
||||
const scrollToSpy = vi.fn()
|
||||
global.window.scrollTo = scrollToSpy
|
||||
;(useLocation as jest.Mock).mockImplementation(() => ({
|
||||
;(useLocation as Mock).mockImplementation(() => ({
|
||||
pathname: '/initial',
|
||||
}))
|
||||
|
||||
@@ -27,7 +30,7 @@ describe('ScrollToTop', () => {
|
||||
|
||||
// Reset the mock to track subsequent calls
|
||||
scrollToSpy.mockReset()
|
||||
;(useLocation as jest.Mock).mockImplementation(() => ({
|
||||
;(useLocation as Mock).mockImplementation(() => ({
|
||||
pathname: '/changed',
|
||||
}))
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import '@testing-library/jest-dom/vitest'
|
||||
|
||||
import { render } from '@testing-library/react'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { TagList } from './TagsList'
|
||||
|
||||
jest.mock('src/common/hooks/useCommonStores', () => ({
|
||||
vi.mock('src/common/hooks/useCommonStores', () => ({
|
||||
__esModule: true,
|
||||
useCommonStores: () => ({
|
||||
stores: {
|
||||
|
||||
@@ -4,7 +4,7 @@ Switch config dependent on use case
|
||||
For our use case the production config is stored in environment variables passed from
|
||||
CI. You can replace this with your own config or use the same pattern to keep
|
||||
api keys secret. Note, create-react-app only passes environment variables prefixed with
|
||||
'REACT_APP'. The required info has been encrypted and stored in a circleCI deployment context.
|
||||
'VITE_APP'. The required info has been encrypted and stored in a circleCI deployment context.
|
||||
|
||||
Dev config is hardcoded - You can find more information about potential security risk here:
|
||||
https://javebratt.com/hide-firebase-api/
|
||||
@@ -17,7 +17,7 @@ import type { IFirebaseConfig, ISentryConfig, siteVariants } from './types'
|
||||
/**
|
||||
* Helper function to load configuration property
|
||||
* from the global configuration object
|
||||
* During the development cycle this will be process.env
|
||||
* During the development cycle this will be import.meta.env
|
||||
* when running this application with the output of `yarn build`
|
||||
* we will instead load from the global window
|
||||
*
|
||||
@@ -27,9 +27,9 @@ import type { IFirebaseConfig, ISentryConfig, siteVariants } from './types'
|
||||
*/
|
||||
const _c = (property: ConfigurationOption, fallbackValue?: string): string => {
|
||||
const configurationSource = ['development', 'test'].includes(
|
||||
process.env.NODE_ENV,
|
||||
import.meta.env.NODE_ENV,
|
||||
)
|
||||
? process.env
|
||||
? import.meta.env
|
||||
: window?.__OA_COMMUNITY_PLATFORM_CONFIGURATION
|
||||
return configurationSource?.[property] || fallbackValue
|
||||
}
|
||||
@@ -62,14 +62,14 @@ const getSiteVariant = (): siteVariants => {
|
||||
}
|
||||
if (
|
||||
location.host === 'localhost:3456' ||
|
||||
_c('REACT_APP_SITE_VARIANT') === 'test-ci'
|
||||
_c('VITE_APP_SITE_VARIANT') === 'test-ci'
|
||||
) {
|
||||
return 'test-ci'
|
||||
}
|
||||
if (_c('REACT_APP_SITE_VARIANT') === 'preview') {
|
||||
if (_c('VITE_APP_SITE_VARIANT') === 'preview') {
|
||||
return 'preview'
|
||||
}
|
||||
switch (_c('REACT_APP_BRANCH')) {
|
||||
switch (_c('VITE_APP_BRANCH')) {
|
||||
case 'production':
|
||||
return 'production'
|
||||
case 'master':
|
||||
@@ -131,12 +131,12 @@ const firebaseConfigs: { [variant in siteVariants]: IFirebaseConfig } = {
|
||||
},
|
||||
/** Production/live backend with released frontend */
|
||||
production: {
|
||||
apiKey: _c('REACT_APP_FIREBASE_API_KEY'),
|
||||
authDomain: _c('REACT_APP_FIREBASE_AUTH_DOMAIN'),
|
||||
databaseURL: _c('REACT_APP_FIREBASE_DATABASE_URL'),
|
||||
messagingSenderId: _c('REACT_APP_FIREBASE_MESSAGING_SENDER_ID'),
|
||||
projectId: _c('REACT_APP_FIREBASE_PROJECT_ID'),
|
||||
storageBucket: _c('REACT_APP_FIREBASE_STORAGE_BUCKET'),
|
||||
apiKey: _c('VITE_APP_FIREBASE_API_KEY'),
|
||||
authDomain: _c('VITE_APP_FIREBASE_AUTH_DOMAIN'),
|
||||
databaseURL: _c('VITE_APP_FIREBASE_DATABASE_URL'),
|
||||
messagingSenderId: _c('VITE_APP_FIREBASE_MESSAGING_SENDER_ID'),
|
||||
projectId: _c('VITE_APP_FIREBASE_PROJECT_ID'),
|
||||
storageBucket: _c('VITE_APP_FIREBASE_STORAGE_BUCKET'),
|
||||
},
|
||||
}
|
||||
/*********************************************************************************************** /
|
||||
@@ -148,22 +148,22 @@ export const DEV_SITE_ROLE = devSiteRole
|
||||
export const FIREBASE_CONFIG = firebaseConfigs[siteVariant]
|
||||
export const SENTRY_CONFIG: ISentryConfig = {
|
||||
dsn: _c(
|
||||
'REACT_APP_SENTRY_DSN',
|
||||
'VITE_APP_SENTRY_DSN',
|
||||
'https://8c1f7eb4892e48b18956af087bdfa3ac@sentry.io/1399729',
|
||||
),
|
||||
environment: siteVariant,
|
||||
}
|
||||
|
||||
export const CDN_URL = _c('REACT_APP_CDN_URL', '')
|
||||
export const VERSION = _c('REACT_APP_PROJECT_VERSION', '')
|
||||
export const GA_TRACKING_ID = _c('REACT_APP_GA_TRACKING_ID')
|
||||
export const PATREON_CLIENT_ID = _c('REACT_APP_PATREON_CLIENT_ID')
|
||||
export const API_URL = _c('REACT_APP_API_URL', '')
|
||||
export const CDN_URL = _c('VITE_APP_CDN_URL', '')
|
||||
export const VERSION = _c('VITE_APP_PROJECT_VERSION', '')
|
||||
export const GA_TRACKING_ID = _c('VITE_APP_GA_TRACKING_ID')
|
||||
export const PATREON_CLIENT_ID = _c('VITE_APP_PATREON_CLIENT_ID')
|
||||
export const API_URL = _c('VITE_APP_API_URL', '')
|
||||
|
||||
export const isPreciousPlastic = (): boolean => {
|
||||
return (
|
||||
(_c('REACT_APP_PLATFORM_THEME') ||
|
||||
localStorage.getItem('platformTheme')) === 'precious-plastic'
|
||||
(_c('VITE_APP_PLATFORM_THEME') || localStorage.getItem('platformTheme')) ===
|
||||
'precious-plastic'
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -5,27 +5,27 @@
|
||||
* type exported from this file
|
||||
*/
|
||||
export const _supportedConfigurationOptions = [
|
||||
'REACT_APP_SENTRY_DSN',
|
||||
'REACT_APP_PROJECT_VERSION',
|
||||
'REACT_APP_GA_TRACKING_ID',
|
||||
'REACT_APP_FIREBASE_API_KEY',
|
||||
'REACT_APP_FIREBASE_AUTH_DOMAIN',
|
||||
'REACT_APP_FIREBASE_DATABASE_URL',
|
||||
'REACT_APP_FIREBASE_MESSAGING_SENDER_ID',
|
||||
'REACT_APP_FIREBASE_PROJECT_ID',
|
||||
'REACT_APP_FIREBASE_STORAGE_BUCKET',
|
||||
'REACT_APP_ALGOLIA_PLACES_APP_ID',
|
||||
'REACT_APP_ALGOLIA_PLACES_API_KEY',
|
||||
'REACT_APP_BRANCH',
|
||||
'REACT_APP_SITE_VARIANT',
|
||||
'REACT_APP_LOG_LEVEL',
|
||||
'REACT_APP_LOG_TRANSPORT',
|
||||
'REACT_APP_SUPPORTED_MODULES',
|
||||
'REACT_APP_PLATFORM_THEME',
|
||||
'REACT_APP_PLATFORM_PROFILES',
|
||||
'REACT_APP_CDN_URL',
|
||||
'REACT_APP_PATREON_CLIENT_ID',
|
||||
'REACT_APP_API_URL',
|
||||
'VITE_APP_SENTRY_DSN',
|
||||
'VITE_APP_PROJECT_VERSION',
|
||||
'VITE_APP_GA_TRACKING_ID',
|
||||
'VITE_APP_FIREBASE_API_KEY',
|
||||
'VITE_APP_FIREBASE_AUTH_DOMAIN',
|
||||
'VITE_APP_FIREBASE_DATABASE_URL',
|
||||
'VITE_APP_FIREBASE_MESSAGING_SENDER_ID',
|
||||
'VITE_APP_FIREBASE_PROJECT_ID',
|
||||
'VITE_APP_FIREBASE_STORAGE_BUCKET',
|
||||
'VITE_APP_ALGOLIA_PLACES_APP_ID',
|
||||
'VITE_APP_ALGOLIA_PLACES_API_KEY',
|
||||
'VITE_APP_BRANCH',
|
||||
'VITE_APP_SITE_VARIANT',
|
||||
'VITE_APP_LOG_LEVEL',
|
||||
'VITE_APP_LOG_TRANSPORT',
|
||||
'VITE_APP_SUPPORTED_MODULES',
|
||||
'VITE_APP_PLATFORM_THEME',
|
||||
'VITE_APP_PLATFORM_PROFILES',
|
||||
'VITE_APP_CDN_URL',
|
||||
'VITE_APP_PATREON_CLIENT_ID',
|
||||
'VITE_APP_API_URL',
|
||||
] as const
|
||||
|
||||
export type ConfigurationOption = typeof _supportedConfigurationOptions[number]
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { describe, it } from 'vitest'
|
||||
|
||||
import { logger } from './index'
|
||||
|
||||
describe('logger', () => {
|
||||
|
||||
@@ -2,8 +2,8 @@ import { Logger } from 'tslog'
|
||||
|
||||
import { getConfigurationOption } from '../config/config'
|
||||
|
||||
const logLevel = getConfigurationOption('REACT_APP_LOG_LEVEL', 'info')
|
||||
const logTransport = getConfigurationOption('REACT_APP_LOG_TRANSPORT', 'none')
|
||||
const logLevel = getConfigurationOption('VITE_APP_LOG_LEVEL', 'info')
|
||||
const logTransport = getConfigurationOption('VITE_APP_LOG_TRANSPORT', 'none')
|
||||
|
||||
const levelNumberToNameMap = {
|
||||
silly: 0,
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import { afterAll, describe, expect, it } from 'vitest'
|
||||
|
||||
import { getSupportedModules, isModuleSupported, MODULE } from '.'
|
||||
|
||||
describe('getSupportedModules', () => {
|
||||
const oldProcessEnv = process.env
|
||||
const defaultModules = import.meta.env.VITE_APP_SUPPORTED_MODULES
|
||||
afterAll(() => {
|
||||
process.env = oldProcessEnv
|
||||
import.meta.env.VITE_APP_SUPPORTED_MODULES = defaultModules
|
||||
})
|
||||
|
||||
it('returns a default set of modules', () => {
|
||||
process.env.REACT_APP_SUPPORTED_MODULES = ''
|
||||
import.meta.env.VITE_APP_SUPPORTED_MODULES = ''
|
||||
expect(getSupportedModules()).toStrictEqual([
|
||||
MODULE.CORE,
|
||||
MODULE.HOWTO,
|
||||
@@ -20,17 +22,17 @@ describe('getSupportedModules', () => {
|
||||
})
|
||||
|
||||
it('loads an additional module based on env configuration', () => {
|
||||
process.env.REACT_APP_SUPPORTED_MODULES = ` ${MODULE.HOWTO} `
|
||||
import.meta.env.VITE_APP_SUPPORTED_MODULES = ` ${MODULE.HOWTO} `
|
||||
expect(getSupportedModules()).toStrictEqual([MODULE.CORE, MODULE.HOWTO])
|
||||
})
|
||||
|
||||
it('loads multiple modules based on env configuration', () => {
|
||||
process.env.REACT_APP_SUPPORTED_MODULES = ` ${MODULE.HOWTO} `
|
||||
import.meta.env.VITE_APP_SUPPORTED_MODULES = ` ${MODULE.HOWTO} `
|
||||
expect(getSupportedModules()).toStrictEqual([MODULE.CORE, MODULE.HOWTO])
|
||||
})
|
||||
|
||||
it('ignores a malformed module definitions', () => {
|
||||
process.env.REACT_APP_SUPPORTED_MODULES = `fake module,${MODULE.HOWTO},malicious `
|
||||
import.meta.env.VITE_APP_SUPPORTED_MODULES = `fake module,${MODULE.HOWTO},malicious `
|
||||
expect(getSupportedModules()).toStrictEqual([MODULE.CORE, MODULE.HOWTO])
|
||||
})
|
||||
})
|
||||
@@ -41,12 +43,12 @@ describe('isModuleSupported', () => {
|
||||
})
|
||||
|
||||
it('returns true for module enabled via env', () => {
|
||||
process.env.REACT_APP_SUPPORTED_MODULES = `${MODULE.RESEARCH}`
|
||||
import.meta.env.VITE_APP_SUPPORTED_MODULES = `${MODULE.RESEARCH}`
|
||||
expect(isModuleSupported(MODULE.RESEARCH)).toBe(true)
|
||||
})
|
||||
|
||||
it('returns false for unsupported module', () => {
|
||||
process.env.REACT_APP_SUPPORTED_MODULES = `${MODULE.HOWTO}`
|
||||
import.meta.env.VITE_APP_SUPPORTED_MODULES = `${MODULE.HOWTO}`
|
||||
expect(isModuleSupported(MODULE.RESEARCH)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -13,7 +13,7 @@ export enum MODULE {
|
||||
export const getSupportedModules = (): MODULE[] => {
|
||||
const envModules: string[] =
|
||||
getConfigurationOption(
|
||||
'REACT_APP_SUPPORTED_MODULES',
|
||||
'VITE_APP_SUPPORTED_MODULES',
|
||||
'howto,map,research,academy,user,question',
|
||||
)
|
||||
.split(',')
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { getSupportedProfileTypes } from './index'
|
||||
import { SupportedProfileTypesFactory } from './SupportedProfileTypesFactory'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
describe('getSupportedProfileTypes', () => {
|
||||
expect(typeof getSupportedProfileTypes).toBe('function')
|
||||
})
|
||||
import { SupportedProfileTypesFactory } from './SupportedProfileTypesFactory'
|
||||
|
||||
describe('SupportedProfileTypesFactory', () => {
|
||||
it('handles malformed input with default items', () => {
|
||||
@@ -13,37 +10,41 @@ describe('SupportedProfileTypesFactory', () => {
|
||||
expect(profiles).toEqual(
|
||||
expect.arrayContaining([
|
||||
{
|
||||
cleanImageSrc: 'avatar_member_sm.svg',
|
||||
cleanImageVerifiedSrc: 'avatar_member_sm.svg',
|
||||
imageSrc: 'avatar_member_sm.svg',
|
||||
cleanImageSrc:
|
||||
'/src/assets/images/themes/precious-plastic/avatar_member_sm.svg',
|
||||
cleanImageVerifiedSrc:
|
||||
'/src/assets/images/themes/precious-plastic/avatar_member_sm.svg',
|
||||
imageSrc:
|
||||
'/src/assets/images/themes/precious-plastic/avatar_member_sm.svg',
|
||||
label: 'member',
|
||||
textLabel: 'I am a member',
|
||||
},
|
||||
{
|
||||
cleanImageSrc: 'map-workspace.svg',
|
||||
cleanImageVerifiedSrc: 'map-workspace-verified.svg',
|
||||
imageSrc: 'pt-workspace.svg',
|
||||
cleanImageSrc: '/src/assets/icons/map-workspace.svg',
|
||||
cleanImageVerifiedSrc: '/src/assets/icons/map-workspace-verified.svg',
|
||||
imageSrc: '/src/assets/images/badges/pt-workspace.svg',
|
||||
label: 'workspace',
|
||||
textLabel: 'I run a workspace',
|
||||
},
|
||||
{
|
||||
cleanImageSrc: 'map-machine.svg',
|
||||
cleanImageVerifiedSrc: 'map-machine-verified.svg',
|
||||
imageSrc: 'pt-machine-shop.svg',
|
||||
cleanImageSrc: '/src/assets/icons/map-machine.svg',
|
||||
cleanImageVerifiedSrc: '/src/assets/icons/map-machine-verified.svg',
|
||||
imageSrc: '/src/assets/images/badges/pt-machine-shop.svg',
|
||||
label: 'machine-builder',
|
||||
textLabel: 'I build machines',
|
||||
},
|
||||
{
|
||||
cleanImageSrc: 'map-community.svg',
|
||||
cleanImageVerifiedSrc: 'map-community-verified.svg',
|
||||
imageSrc: 'pt-local-community.svg',
|
||||
cleanImageSrc: '/src/assets/icons/map-community.svg',
|
||||
cleanImageVerifiedSrc: '/src/assets/icons/map-community-verified.svg',
|
||||
imageSrc: '/src/assets/images/badges/pt-local-community.svg',
|
||||
label: 'community-builder',
|
||||
textLabel: 'I run a local community',
|
||||
},
|
||||
{
|
||||
cleanImageSrc: 'map-collection.svg',
|
||||
cleanImageVerifiedSrc: 'map-collection-verified.svg',
|
||||
imageSrc: 'pt-collection-point.svg',
|
||||
cleanImageSrc: '/src/assets/icons/map-collection.svg',
|
||||
cleanImageVerifiedSrc:
|
||||
'/src/assets/icons/map-collection-verified.svg',
|
||||
imageSrc: '/src/assets/images/badges/pt-collection-point.svg',
|
||||
label: 'collection-point',
|
||||
textLabel: 'I collect & sort plastic',
|
||||
},
|
||||
@@ -57,9 +58,12 @@ describe('SupportedProfileTypesFactory', () => {
|
||||
expect(profiles).toEqual(
|
||||
expect.arrayContaining([
|
||||
{
|
||||
cleanImageSrc: 'avatar_member_sm.svg',
|
||||
cleanImageVerifiedSrc: 'avatar_member_sm.svg',
|
||||
imageSrc: 'avatar_member_sm.svg',
|
||||
cleanImageSrc:
|
||||
'/src/assets/images/themes/precious-plastic/avatar_member_sm.svg',
|
||||
cleanImageVerifiedSrc:
|
||||
'/src/assets/images/themes/precious-plastic/avatar_member_sm.svg',
|
||||
imageSrc:
|
||||
'/src/assets/images/themes/precious-plastic/avatar_member_sm.svg',
|
||||
label: 'member',
|
||||
textLabel: 'I am a member',
|
||||
},
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { PlatformTheme } from 'oa-themes'
|
||||
|
||||
export const getSupportedProfileTypes = (currentTheme?: PlatformTheme) => {
|
||||
const supportedProfileTypes = SupportedProfileTypesFactory(
|
||||
getConfigurationOption('REACT_APP_PLATFORM_PROFILES', ''),
|
||||
getConfigurationOption('VITE_APP_PLATFORM_PROFILES', ''),
|
||||
currentTheme,
|
||||
)()
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { getFrameSrc } from './Academy'
|
||||
|
||||
// Mock out the useCommonStores method
|
||||
@@ -5,9 +7,9 @@ import { getFrameSrc } from './Academy'
|
||||
// being instantiated as part of the loading process
|
||||
// This is a code smell, which needs to be resolved but
|
||||
// is out of scope for the current task.
|
||||
jest.mock('src/common/hooks/useCommonStores', () => {
|
||||
vi.mock('src/common/hooks/useCommonStores', () => {
|
||||
return {
|
||||
useCommonStores: jest.fn(),
|
||||
useCommonStores: vi.fn(),
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { render } from '@testing-library/react'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import DonationThankYou from './DonationThankYou'
|
||||
|
||||
describe('DonationThankYou', () => {
|
||||
it('sends the expected message', () => {
|
||||
window.top.postMessage = jest.fn()
|
||||
window.top.postMessage = vi.fn()
|
||||
|
||||
render(<DonationThankYou />)
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { HowtoProvider } from 'src/test/components'
|
||||
import { describe, it } from 'vitest'
|
||||
|
||||
import { FormFieldWrapper } from '.'
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import '@testing-library/jest-dom/vitest'
|
||||
|
||||
import { MemoryRouter } from 'react-router-dom'
|
||||
import { ThemeProvider } from '@emotion/react'
|
||||
import { act, fireEvent, render } from '@testing-library/react'
|
||||
@@ -5,12 +7,13 @@ import { Provider } from 'mobx-react'
|
||||
import { useCommonStores } from 'src/common/hooks/useCommonStores'
|
||||
import { FactoryHowto } from 'src/test/factories/Howto'
|
||||
import { testingThemeStyles } from 'src/test/utils/themeUtils'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { HowtoForm } from './Howto.form'
|
||||
|
||||
const Theme = testingThemeStyles
|
||||
|
||||
jest.mock('src/common/hooks/useCommonStores', () => {
|
||||
vi.mock('src/common/hooks/useCommonStores', () => {
|
||||
return {
|
||||
useCommonStores: () => ({
|
||||
stores: {
|
||||
@@ -26,8 +29,8 @@ jest.mock('src/common/hooks/useCommonStores', () => {
|
||||
Database: false,
|
||||
Complete: false,
|
||||
},
|
||||
validateTitleForSlug: jest.fn(),
|
||||
uploadHowTo: jest.fn(),
|
||||
validateTitleForSlug: vi.fn(),
|
||||
uploadHowTo: vi.fn(),
|
||||
},
|
||||
tagsStore: {
|
||||
allTags: [
|
||||
@@ -49,8 +52,8 @@ describe('Howto form', () => {
|
||||
const formValues = FactoryHowto()
|
||||
// Act
|
||||
let wrapper
|
||||
await act(async () => {
|
||||
wrapper = await Wrapper(formValues, 'edit', {})
|
||||
act(() => {
|
||||
wrapper = Wrapper(formValues, 'edit', {})
|
||||
})
|
||||
|
||||
// Assert
|
||||
@@ -156,7 +159,7 @@ describe('Howto form', () => {
|
||||
})
|
||||
})
|
||||
|
||||
const Wrapper = async (formValues, parentType, navProps) => {
|
||||
const Wrapper = (formValues, parentType, navProps) => {
|
||||
return render(
|
||||
<Provider {...useCommonStores().stores}>
|
||||
<ThemeProvider theme={Theme}>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { render, screen } from '@testing-library/react'
|
||||
import { guidance } from 'src/pages/Howto/labels'
|
||||
import { HowtoProvider } from 'src/test/components'
|
||||
import { FactoryCategory } from 'src/test/factories/Category'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { HowtoCategoryGuidance } from '.'
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { HowtoProvider } from 'src/test/components'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { HowtoErrors } from '.'
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { HowtoProvider } from 'src/test/components'
|
||||
import { describe, it } from 'vitest'
|
||||
|
||||
import { HowtoFieldDescription } from '.'
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { render, screen } from '@testing-library/react'
|
||||
import { guidance } from 'src/pages/Howto/labels'
|
||||
import { HowtoProvider } from 'src/test/components'
|
||||
import { FactoryCategory } from 'src/test/factories/Category'
|
||||
import { describe, it, vi } from 'vitest'
|
||||
|
||||
import { HowtoFieldFiles } from '.'
|
||||
|
||||
@@ -11,7 +12,7 @@ describe('HowtoFieldFiles', () => {
|
||||
category: undefined,
|
||||
fileEditMode: true,
|
||||
files: [],
|
||||
onClick: jest.fn(),
|
||||
onClick: vi.fn(),
|
||||
showInvalidFileWarning: false,
|
||||
}
|
||||
|
||||
@@ -31,7 +32,7 @@ describe('HowtoFieldFiles', () => {
|
||||
category: FactoryCategory,
|
||||
fileEditMode: true,
|
||||
files: [],
|
||||
onClick: jest.fn(),
|
||||
onClick: vi.fn(),
|
||||
showInvalidFileWarning: false,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { HowtoProvider } from 'src/test/components'
|
||||
import { describe, it } from 'vitest'
|
||||
|
||||
import { HowtoFieldStepsContainer } from '.'
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { HowtoProvider } from 'src/test/components'
|
||||
import { describe, it, vi } from 'vitest'
|
||||
|
||||
import { HowtoFieldTitle } from '.'
|
||||
|
||||
import type { HowtoStore } from 'src/stores/Howto/howto.store'
|
||||
import type { ParentType } from './Howto.form'
|
||||
|
||||
jest.mock('src/stores/Howto/howto.store')
|
||||
const store = jest.createMockFromModule('src/stores/Howto/howto.store')
|
||||
vi.mock('src/stores/Howto/howto.store')
|
||||
const store = await vi.importMock('src/stores/Howto/howto.store')
|
||||
|
||||
describe('HowtoFieldTitle', () => {
|
||||
it('renders', async () => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { HowtoProvider } from 'src/test/components'
|
||||
import { describe, it, vi } from 'vitest'
|
||||
|
||||
import { HowtoFieldStep } from '.'
|
||||
|
||||
@@ -9,8 +10,8 @@ describe('HowtoFieldStep', () => {
|
||||
step: [],
|
||||
index: 0,
|
||||
images: [],
|
||||
onDelete: jest.fn(() => null),
|
||||
moveStep: jest.fn(() => null),
|
||||
onDelete: vi.fn(() => null),
|
||||
moveStep: vi.fn(() => null),
|
||||
}
|
||||
|
||||
render(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { intro, steps } from 'src/pages/Howto/labels'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { transformHowtoErrors } from '.'
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import '@testing-library/jest-dom/vitest'
|
||||
|
||||
import {
|
||||
createMemoryRouter,
|
||||
createRoutesFromElements,
|
||||
@@ -10,6 +12,7 @@ import { act, render, within } from '@testing-library/react'
|
||||
import { Provider } from 'mobx-react'
|
||||
import { preciousPlasticTheme } from 'oa-themes'
|
||||
import { FactoryHowto, FactoryHowtoStep } from 'src/test/factories/Howto'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import type { HowtoStore } from 'src/stores/Howto/howto.store'
|
||||
|
||||
@@ -18,21 +21,21 @@ const Theme = preciousPlasticTheme.styles
|
||||
const howto = FactoryHowto()
|
||||
|
||||
const mockHowtoStore = () => ({
|
||||
setActiveHowtoBySlug: jest.fn(),
|
||||
setActiveHowtoBySlug: vi.fn(),
|
||||
activeHowto: howto,
|
||||
needsModeration: jest.fn().mockReturnValue(false),
|
||||
incrementViewCount: jest.fn(),
|
||||
removeActiveHowto: jest.fn(),
|
||||
needsModeration: vi.fn().mockReturnValue(false),
|
||||
incrementViewCount: vi.fn(),
|
||||
removeActiveHowto: vi.fn(),
|
||||
})
|
||||
|
||||
jest.mock('src/common/hooks/useCommonStores', () => ({
|
||||
vi.mock('src/common/hooks/useCommonStores', () => ({
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
__esModule: true,
|
||||
useCommonStores: () => ({
|
||||
stores: {
|
||||
userStore: {},
|
||||
aggregationsStore: {
|
||||
isVerified: jest.fn((userId) => userId === 'HowtoAuthor'),
|
||||
isVerified: vi.fn((userId) => userId === 'HowtoAuthor'),
|
||||
users_verified: {
|
||||
HowtoAuthor: true,
|
||||
},
|
||||
|
||||
@@ -1,40 +1,42 @@
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
import { UserRole } from 'oa-shared/models'
|
||||
import { UserRole } from 'oa-shared'
|
||||
import { FactoryUser } from 'src/test/factories/User'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { exportedForTesting } from './howto.service'
|
||||
|
||||
const mockWhere = jest.fn()
|
||||
const mockOrderBy = jest.fn()
|
||||
const mockLimit = jest.fn()
|
||||
const mockOr = jest.fn()
|
||||
jest.mock('firebase/firestore', () => ({
|
||||
collection: jest.fn(),
|
||||
query: jest.fn(),
|
||||
and: jest.fn(),
|
||||
const mockWhere = vi.fn()
|
||||
const mockOrderBy = vi.fn()
|
||||
const mockLimit = vi.fn()
|
||||
const mockOr = vi.fn()
|
||||
vi.mock('firebase/firestore', () => ({
|
||||
collection: vi.fn(),
|
||||
query: vi.fn(),
|
||||
and: vi.fn(),
|
||||
where: (path, op, value) => mockWhere(path, op, value),
|
||||
limit: (limit) => mockLimit(limit),
|
||||
orderBy: (field, direction) => mockOrderBy(field, direction),
|
||||
or: (constraints) => mockOr(constraints),
|
||||
}))
|
||||
|
||||
jest.mock('../../stores/databaseV2/endpoints', () => ({
|
||||
vi.mock('../../stores/databaseV2/endpoints', () => ({
|
||||
DB_ENDPOINTS: {
|
||||
howtos: 'howtos',
|
||||
categories: 'categories',
|
||||
},
|
||||
}))
|
||||
|
||||
jest.mock('../../config/config', () => ({
|
||||
getConfigurationOption: jest.fn(),
|
||||
vi.mock('../../config/config', () => ({
|
||||
getConfigurationOption: vi.fn(),
|
||||
FIREBASE_CONFIG: {
|
||||
apiKey: 'AIyChVN',
|
||||
databaseURL: 'https://test.firebaseio.com',
|
||||
projectId: 'test',
|
||||
storageBucket: 'test.appspot.com',
|
||||
},
|
||||
localStorage: jest.fn(),
|
||||
localStorage: vi.fn(),
|
||||
SITE: 'unit-tests',
|
||||
}))
|
||||
|
||||
describe('howtos.search', () => {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import '@testing-library/jest-dom/vitest'
|
||||
|
||||
import {
|
||||
createMemoryRouter,
|
||||
createRoutesFromElements,
|
||||
@@ -12,6 +14,7 @@ import { useCommonStores } from 'src/common/hooks/useCommonStores'
|
||||
import { FactoryMapPin } from 'src/test/factories/MapPin'
|
||||
import { FactoryUser } from 'src/test/factories/User'
|
||||
import { testingThemeStyles } from 'src/test/utils/themeUtils'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { MapPinServiceContext } from './map.service'
|
||||
import Maps from './Maps'
|
||||
@@ -20,17 +23,17 @@ import type { IMapPinService } from './map.service'
|
||||
|
||||
const Theme = testingThemeStyles
|
||||
|
||||
jest.mock('src/common/hooks/useCommonStores', () => ({
|
||||
vi.mock('src/common/hooks/useCommonStores', () => ({
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
__esModule: true,
|
||||
useCommonStores: () => ({
|
||||
stores: {
|
||||
userStore: {
|
||||
getUserProfile: jest.fn(),
|
||||
updateUserBadge: jest.fn(),
|
||||
getUserProfile: vi.fn(),
|
||||
updateUserBadge: vi.fn(),
|
||||
},
|
||||
aggregationsStore: {
|
||||
isVerified: jest.fn(),
|
||||
isVerified: vi.fn(),
|
||||
users_verified: {
|
||||
HowtoAuthor: true,
|
||||
},
|
||||
@@ -52,7 +55,7 @@ describe('Maps', () => {
|
||||
Object.defineProperty(global.navigator, 'geolocation', {
|
||||
writable: true,
|
||||
value: {
|
||||
getCurrentPosition: jest.fn(),
|
||||
getCurrentPosition: vi.fn(),
|
||||
},
|
||||
})
|
||||
|
||||
@@ -86,7 +89,7 @@ const Wrapper = async (path = '/map') => {
|
||||
)
|
||||
|
||||
const mockMapPinService: IMapPinService = {
|
||||
getMapPinByUserId: jest.fn().mockResolvedValue({
|
||||
getMapPinByUserId: vi.fn().mockResolvedValue({
|
||||
...FactoryUser({
|
||||
moderation: IModerationStatus.ACCEPTED,
|
||||
}),
|
||||
@@ -95,8 +98,8 @@ const Wrapper = async (path = '/map') => {
|
||||
shortDescription: 'description',
|
||||
},
|
||||
}),
|
||||
getMapPinSelf: jest.fn().mockResolvedValue({}),
|
||||
getMapPins: jest.fn().mockImplementation(() => {
|
||||
getMapPinSelf: vi.fn().mockResolvedValue({}),
|
||||
getMapPins: vi.fn().mockImplementation(() => {
|
||||
return Promise.resolve([])
|
||||
}),
|
||||
}
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import { faker } from '@faker-js/faker'
|
||||
import { DB_ENDPOINTS } from 'src/models'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { mapPinService } from './map.service'
|
||||
|
||||
const mockWhere = jest.fn()
|
||||
const mockLimit = jest.fn()
|
||||
const mockQuery = jest.fn()
|
||||
const mockCollection = jest.fn()
|
||||
const mockWhere = vi.fn()
|
||||
const mockLimit = vi.fn()
|
||||
const mockQuery = vi.fn()
|
||||
const mockCollection = vi.fn()
|
||||
|
||||
const mockQuerySnapshot = {
|
||||
docs: [{ data: () => {} }],
|
||||
}
|
||||
|
||||
const mockGetDocs = jest.fn().mockResolvedValue(mockQuerySnapshot)
|
||||
jest.mock('firebase/firestore', () => ({
|
||||
const mockGetDocs = vi.fn().mockResolvedValue(mockQuerySnapshot)
|
||||
vi.mock('firebase/firestore', () => ({
|
||||
collection: (_firebase, connectionName) => mockCollection(connectionName),
|
||||
query: (collectionRef, whereResult) => mockQuery(collectionRef, whereResult),
|
||||
where: (path, op, value) => mockWhere(path, op, value),
|
||||
@@ -25,7 +26,7 @@ describe('map.service', () => {
|
||||
describe('getMapPins', () => {
|
||||
it('fetches map pins', async () => {
|
||||
// prepare
|
||||
global.fetch = jest.fn().mockResolvedValue({
|
||||
global.fetch = vi.fn().mockResolvedValue({
|
||||
json: () => Promise.resolve([{ _id: '1' }]),
|
||||
})
|
||||
|
||||
@@ -38,7 +39,7 @@ describe('map.service', () => {
|
||||
|
||||
it('handles errors', async () => {
|
||||
// prepare
|
||||
global.fetch = jest.fn().mockRejectedValue('error')
|
||||
global.fetch = vi.fn().mockRejectedValue('error')
|
||||
|
||||
// act
|
||||
const result = await mapPinService.getMapPins()
|
||||
@@ -51,7 +52,7 @@ describe('map.service', () => {
|
||||
describe('getMapPinByUserId', () => {
|
||||
it('fetches map pin by user id', async () => {
|
||||
// prepare
|
||||
global.fetch = jest.fn().mockResolvedValue({
|
||||
global.fetch = vi.fn().mockResolvedValue({
|
||||
json: () => Promise.resolve({ _id: '1' }),
|
||||
})
|
||||
|
||||
@@ -64,7 +65,7 @@ describe('map.service', () => {
|
||||
|
||||
it('handles errors', async () => {
|
||||
// prepare
|
||||
global.fetch = jest.fn().mockRejectedValue('error')
|
||||
global.fetch = vi.fn().mockRejectedValue('error')
|
||||
|
||||
// act
|
||||
const result = await mapPinService.getMapPinByUserId('1')
|
||||
|
||||
@@ -16,6 +16,10 @@ export const QuestionCategoryField = () => {
|
||||
useEffect(() => {
|
||||
const initCategories = async () => {
|
||||
const categories = await questionService.getQuestionCategories()
|
||||
if (!categories) {
|
||||
return
|
||||
}
|
||||
|
||||
const selectOptions = categories.map((category) => ({
|
||||
value: category,
|
||||
label: category.label,
|
||||
|
||||
@@ -62,16 +62,18 @@ export const QuestionListing = () => {
|
||||
ITEMS_PER_PAGE,
|
||||
)
|
||||
|
||||
if (skipFrom) {
|
||||
// if skipFrom is set, means we are requesting another page that should be appended
|
||||
setQuestions((questions) => [...questions, ...result.items])
|
||||
} else {
|
||||
setQuestions(result.items)
|
||||
if (result) {
|
||||
if (skipFrom) {
|
||||
// if skipFrom is set, means we are requesting another page that should be appended
|
||||
setQuestions((questions) => [...questions, ...result.items])
|
||||
} else {
|
||||
setQuestions(result.items)
|
||||
}
|
||||
|
||||
setLastVisible(result.lastVisible)
|
||||
|
||||
setTotal(result.total)
|
||||
}
|
||||
|
||||
setLastVisible(result.lastVisible)
|
||||
|
||||
setTotal(result.total)
|
||||
} catch (error) {
|
||||
logger.error('error fetching questions', error)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import '@testing-library/jest-dom/vitest'
|
||||
|
||||
import {
|
||||
createMemoryRouter,
|
||||
createRoutesFromElements,
|
||||
@@ -14,9 +16,12 @@ import { FactoryDiscussion } from 'src/test/factories/Discussion'
|
||||
import { FactoryQuestionItem } from 'src/test/factories/Question'
|
||||
import { FactoryUser } from 'src/test/factories/User'
|
||||
import { testingThemeStyles } from 'src/test/utils/themeUtils'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { QuestionPage } from './QuestionPage'
|
||||
|
||||
import type { Mock } from 'vitest'
|
||||
|
||||
const Theme = testingThemeStyles
|
||||
|
||||
const activeUser = FactoryUser({
|
||||
@@ -29,21 +34,21 @@ const mockQuestionItem = FactoryQuestionItem({
|
||||
})
|
||||
const mockDiscussionItem = FactoryDiscussion()
|
||||
|
||||
jest.mock('src/common/hooks/useCommonStores', () => ({
|
||||
vi.mock('src/common/hooks/useCommonStores', () => ({
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
__esModule: true,
|
||||
useCommonStores: () => ({
|
||||
stores: {
|
||||
userStore: {
|
||||
getUserByUsername: jest.fn().mockResolvedValue(mockUser),
|
||||
getUserByUsername: vi.fn().mockResolvedValue(mockUser),
|
||||
},
|
||||
aggregationsStore: {
|
||||
isVerified: jest.fn(),
|
||||
isVerified: vi.fn(),
|
||||
users_verified: {},
|
||||
},
|
||||
tagsStore: {},
|
||||
discussionStore: {
|
||||
fetchOrCreateDiscussionBySource: jest.fn().mockResolvedValue({
|
||||
fetchOrCreateDiscussionBySource: vi.fn().mockResolvedValue({
|
||||
mockDiscussionItem,
|
||||
}),
|
||||
activeUser: mockUser,
|
||||
@@ -52,8 +57,8 @@ jest.mock('src/common/hooks/useCommonStores', () => ({
|
||||
}),
|
||||
}))
|
||||
|
||||
jest.mock('src/stores/Question/question.store')
|
||||
jest.mock('src/stores/Discussions/discussions.store')
|
||||
vi.mock('src/stores/Question/question.store')
|
||||
vi.mock('src/stores/Discussions/discussions.store')
|
||||
|
||||
describe('Questions', () => {
|
||||
let mockQuestionStore
|
||||
@@ -62,20 +67,20 @@ describe('Questions', () => {
|
||||
// Setup a fresh instance of the mock store before each test
|
||||
mockQuestionStore = {
|
||||
activeQuestionItem: mockQuestionItem,
|
||||
incrementViewCount: jest.fn(),
|
||||
fetchQuestionBySlug: jest.fn(() => {
|
||||
incrementViewCount: vi.fn(),
|
||||
fetchQuestionBySlug: vi.fn(() => {
|
||||
return mockQuestionItem
|
||||
}),
|
||||
upsertQuestion: jest.fn(),
|
||||
toggleSubscriberStatusByUserName: jest.fn(),
|
||||
toggleUsefulByUser: jest.fn(),
|
||||
upsertQuestion: vi.fn(),
|
||||
toggleSubscriberStatusByUserName: vi.fn(),
|
||||
toggleUsefulByUser: vi.fn(),
|
||||
}
|
||||
;(useQuestionStore as jest.Mock).mockReturnValue(mockQuestionStore)
|
||||
;(useQuestionStore as Mock).mockReturnValue(mockQuestionStore)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
// Clear all mocks after each test to ensure there's no leakage between tests
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('Breadcrumbs', () => {
|
||||
@@ -91,10 +96,10 @@ describe('Questions', () => {
|
||||
_deleted: faker.datatype.boolean(),
|
||||
_contentModifiedTimestamp: faker.date.past().toString(),
|
||||
}
|
||||
;(useQuestionStore as jest.Mock).mockReturnValue({
|
||||
;(useQuestionStore as Mock).mockReturnValue({
|
||||
...mockQuestionStore,
|
||||
activeQuestionItem: mockQuestionItem,
|
||||
fetchQuestionBySlug: jest.fn(() => {
|
||||
fetchQuestionBySlug: vi.fn(() => {
|
||||
return mockQuestionItem
|
||||
}),
|
||||
})
|
||||
@@ -130,10 +135,10 @@ describe('Questions', () => {
|
||||
mockQuestionItem.title =
|
||||
'Do you prefer camping near a lake or in a forest?'
|
||||
mockQuestionItem.questionCategory = undefined
|
||||
;(useQuestionStore as jest.Mock).mockReturnValue({
|
||||
;(useQuestionStore as Mock).mockReturnValue({
|
||||
...mockQuestionStore,
|
||||
activeQuestionItem: mockQuestionItem,
|
||||
fetchQuestionBySlug: jest.fn(() => {
|
||||
fetchQuestionBySlug: vi.fn(() => {
|
||||
return mockQuestionItem
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
/* eslint-disable max-classes-per-file */
|
||||
jest.mock('../../stores/common/module.store')
|
||||
jest.mock('src/utils/validators')
|
||||
|
||||
import '@testing-library/jest-dom'
|
||||
import '@testing-library/jest-dom/vitest'
|
||||
|
||||
import {
|
||||
createMemoryRouter,
|
||||
@@ -22,10 +18,15 @@ import { FactoryDiscussion } from 'src/test/factories/Discussion'
|
||||
import { FactoryQuestionItem } from 'src/test/factories/Question'
|
||||
import { FactoryUser } from 'src/test/factories/User'
|
||||
import { testingThemeStyles } from 'src/test/utils/themeUtils'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { questionRouteElements } from './question.routes'
|
||||
|
||||
import type { QuestionStore } from 'src/stores/Question/question.store'
|
||||
import type { Mock } from 'vitest'
|
||||
|
||||
vi.mock('../../stores/common/module.store')
|
||||
vi.mock('src/utils/validators')
|
||||
|
||||
const Theme = testingThemeStyles
|
||||
let mockActiveUser = FactoryUser()
|
||||
@@ -33,7 +34,7 @@ const mockDiscussionItem = FactoryDiscussion()
|
||||
|
||||
// Similar to issues in Academy.test.tsx - stub methods called in user store constructor
|
||||
// TODO - replace with mock store or avoid direct call
|
||||
jest.mock('src/common/hooks/useCommonStores', () => ({
|
||||
vi.mock('src/common/hooks/useCommonStores', () => ({
|
||||
__esModule: true,
|
||||
useCommonStores: () => ({
|
||||
stores: {
|
||||
@@ -41,7 +42,7 @@ jest.mock('src/common/hooks/useCommonStores', () => ({
|
||||
user: mockActiveUser,
|
||||
},
|
||||
aggregationsStore: {
|
||||
isVerified: jest.fn(),
|
||||
isVerified: vi.fn(),
|
||||
users_verified: {
|
||||
HowtoAuthor: true,
|
||||
},
|
||||
@@ -59,50 +60,50 @@ jest.mock('src/common/hooks/useCommonStores', () => ({
|
||||
allQuestionCategories: [],
|
||||
},
|
||||
discussionStore: {
|
||||
fetchOrCreateDiscussionBySource: jest.fn().mockResolvedValue({
|
||||
fetchOrCreateDiscussionBySource: vi.fn().mockResolvedValue({
|
||||
mockDiscussionItem,
|
||||
}),
|
||||
activeUser: jest.fn().mockResolvedValue(mockActiveUser),
|
||||
activeUser: vi.fn().mockResolvedValue(mockActiveUser),
|
||||
},
|
||||
},
|
||||
}),
|
||||
}))
|
||||
|
||||
const mockedUsedNavigate = jest.fn()
|
||||
jest.mock('react-router-dom', () => ({
|
||||
...(jest.requireActual('react-router-dom') as any),
|
||||
const mockedUsedNavigate = vi.fn()
|
||||
vi.mock('react-router-dom', async () => ({
|
||||
...((await vi.importActual('react-router-dom')) as any),
|
||||
useNavigate: () => mockedUsedNavigate,
|
||||
}))
|
||||
|
||||
class mockQuestionStoreClass implements Partial<QuestionStore> {
|
||||
setActiveQuestionItemBySlug = jest.fn()
|
||||
needsModeration = jest.fn().mockResolvedValue(true)
|
||||
incrementViewCount = jest.fn()
|
||||
setActiveQuestionItemBySlug = vi.fn()
|
||||
needsModeration = vi.fn().mockResolvedValue(true)
|
||||
incrementViewCount = vi.fn()
|
||||
activeQuestionItem = FactoryQuestionItem({
|
||||
title: 'Question article title',
|
||||
})
|
||||
QuestionUploadStatus = {} as any
|
||||
updateUploadStatus = {} as any
|
||||
formatQuestionCommentList = jest.fn()
|
||||
getActiveQuestionUpdateComments = jest.fn()
|
||||
lockQuestionItem = jest.fn()
|
||||
lockQuestionUpdate = jest.fn()
|
||||
unlockQuestionUpdate = jest.fn()
|
||||
upsertQuestion = jest.fn()
|
||||
fetchQuestions = jest.fn().mockResolvedValue([])
|
||||
fetchQuestionBySlug = jest.fn()
|
||||
formatQuestionCommentList = vi.fn()
|
||||
getActiveQuestionUpdateComments = vi.fn()
|
||||
lockQuestionItem = vi.fn()
|
||||
lockQuestionUpdate = vi.fn()
|
||||
unlockQuestionUpdate = vi.fn()
|
||||
upsertQuestion = vi.fn()
|
||||
fetchQuestions = vi.fn().mockResolvedValue([])
|
||||
fetchQuestionBySlug = vi.fn()
|
||||
votedUsefulCount = 0
|
||||
subscriberCount = 0
|
||||
userCanEditQuestion = true
|
||||
}
|
||||
|
||||
const mockQuestionService: typeof questionService = {
|
||||
getQuestionCategories: jest.fn(() => {
|
||||
getQuestionCategories: vi.fn(() => {
|
||||
return new Promise((resolve) => {
|
||||
resolve([])
|
||||
})
|
||||
}),
|
||||
search: jest.fn(() => {
|
||||
search: vi.fn(() => {
|
||||
return new Promise((resolve) => {
|
||||
resolve({ items: [], total: 0, lastVisible: undefined })
|
||||
})
|
||||
@@ -110,25 +111,25 @@ const mockQuestionService: typeof questionService = {
|
||||
}
|
||||
const mockQuestionStore = new mockQuestionStoreClass()
|
||||
|
||||
jest.mock('src/stores/Question/question.store')
|
||||
jest.mock('src/stores/Discussions/discussions.store')
|
||||
jest.mock('src/pages/Question/question.service')
|
||||
vi.mock('src/stores/Question/question.store')
|
||||
vi.mock('src/stores/Discussions/discussions.store')
|
||||
vi.mock('src/pages/Question/question.service')
|
||||
|
||||
describe('question.routes', () => {
|
||||
beforeEach(() => {
|
||||
;(useQuestionStore as jest.Mock).mockReturnValue(mockQuestionStore)
|
||||
questionService.getQuestionCategories = jest.fn().mockResolvedValue([])
|
||||
;(useQuestionStore as Mock).mockReturnValue(mockQuestionStore)
|
||||
questionService.getQuestionCategories = vi.fn().mockResolvedValue([])
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks()
|
||||
vi.restoreAllMocks()
|
||||
cleanup()
|
||||
})
|
||||
|
||||
describe('/questions/', () => {
|
||||
it('renders a loading state', async () => {
|
||||
let wrapper
|
||||
mockQuestionService.search = jest.fn(() => {
|
||||
mockQuestionService.search = vi.fn(() => {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(
|
||||
() => resolve({ items: [], total: 0, lastVisible: undefined }),
|
||||
@@ -169,7 +170,7 @@ describe('question.routes', () => {
|
||||
const questionTitle = faker.lorem.words(3)
|
||||
const questionSlug = faker.lorem.slug()
|
||||
|
||||
questionService.search = jest.fn(() => {
|
||||
questionService.search = vi.fn(() => {
|
||||
return new Promise((resolve) => {
|
||||
resolve({
|
||||
items: [
|
||||
@@ -205,7 +206,7 @@ describe('question.routes', () => {
|
||||
it('allows user to create a question', async () => {
|
||||
let wrapper
|
||||
// Arrange
|
||||
const mockUpsertQuestion = jest.fn().mockResolvedValue({
|
||||
const mockUpsertQuestion = vi.fn().mockResolvedValue({
|
||||
slug: 'question-title',
|
||||
})
|
||||
useQuestionStore.mockReturnValue({
|
||||
@@ -249,8 +250,8 @@ describe('question.routes', () => {
|
||||
let wrapper
|
||||
const question = FactoryQuestionItem()
|
||||
const activeUser = FactoryUser({})
|
||||
const mockFetchQuestionBySlug = jest.fn().mockResolvedValue(question)
|
||||
const mockIncrementViewCount = jest.fn()
|
||||
const mockFetchQuestionBySlug = vi.fn().mockResolvedValue(question)
|
||||
const mockIncrementViewCount = vi.fn()
|
||||
useQuestionStore.mockReturnValue({
|
||||
...mockQuestionStore,
|
||||
activeUser,
|
||||
@@ -292,7 +293,7 @@ describe('question.routes', () => {
|
||||
const question = FactoryQuestionItem({
|
||||
subscribers: [user.userName],
|
||||
})
|
||||
const mockFetchQuestionBySlug = jest.fn().mockResolvedValue(question)
|
||||
const mockFetchQuestionBySlug = vi.fn().mockResolvedValue(question)
|
||||
useQuestionStore.mockReturnValue({
|
||||
...mockQuestionStore,
|
||||
activeUser: user,
|
||||
@@ -312,7 +313,7 @@ describe('question.routes', () => {
|
||||
it('supports follow behaviour', async () => {
|
||||
let wrapper
|
||||
const question = FactoryQuestionItem()
|
||||
const mockFetchQuestionBySlug = jest.fn().mockResolvedValue(question)
|
||||
const mockFetchQuestionBySlug = vi.fn().mockResolvedValue(question)
|
||||
useQuestionStore.mockReturnValue({
|
||||
...mockQuestionStore,
|
||||
fetchQuestionBySlug: mockFetchQuestionBySlug,
|
||||
@@ -330,7 +331,7 @@ describe('question.routes', () => {
|
||||
let wrapper
|
||||
mockActiveUser = FactoryUser()
|
||||
const question = FactoryQuestionItem()
|
||||
const mockFetchQuestionBySlug = jest.fn().mockResolvedValue(question)
|
||||
const mockFetchQuestionBySlug = vi.fn().mockResolvedValue(question)
|
||||
useQuestionStore.mockReturnValue({
|
||||
...mockQuestionStore,
|
||||
fetchQuestionBySlug: mockFetchQuestionBySlug,
|
||||
@@ -356,7 +357,7 @@ describe('question.routes', () => {
|
||||
_createdBy: mockActiveUser.userName,
|
||||
})
|
||||
|
||||
const mockFetchQuestionBySlug = jest.fn().mockResolvedValue(question)
|
||||
const mockFetchQuestionBySlug = vi.fn().mockResolvedValue(question)
|
||||
|
||||
useQuestionStore.mockReturnValue({
|
||||
...mockQuestionStore,
|
||||
@@ -402,13 +403,13 @@ describe('question.routes', () => {
|
||||
title: faker.lorem.words(1),
|
||||
_createdBy: 'author',
|
||||
})
|
||||
const mockUpsertQuestion = jest.fn().mockResolvedValue({
|
||||
const mockUpsertQuestion = vi.fn().mockResolvedValue({
|
||||
slug: 'question-title',
|
||||
})
|
||||
|
||||
useQuestionStore.mockReturnValue({
|
||||
...mockQuestionStore,
|
||||
fetchQuestionBySlug: jest.fn().mockResolvedValue(questionItem),
|
||||
fetchQuestionBySlug: vi.fn().mockResolvedValue(questionItem),
|
||||
upsertQuestion: mockUpsertQuestion,
|
||||
activeUser: mockActiveUser,
|
||||
})
|
||||
@@ -456,7 +457,7 @@ describe('question.routes', () => {
|
||||
|
||||
useQuestionStore.mockReturnValue({
|
||||
...mockQuestionStore,
|
||||
fetchQuestionBySlug: jest.fn().mockResolvedValue(
|
||||
fetchQuestionBySlug: vi.fn().mockResolvedValue(
|
||||
FactoryQuestionItem({
|
||||
slug: 'slug',
|
||||
_createdBy: 'author',
|
||||
|
||||
@@ -1,35 +1,38 @@
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { exportedForTesting } from './question.service'
|
||||
|
||||
const mockWhere = jest.fn()
|
||||
const mockOrderBy = jest.fn()
|
||||
const mockLimit = jest.fn()
|
||||
jest.mock('firebase/firestore', () => ({
|
||||
collection: jest.fn(),
|
||||
query: jest.fn(),
|
||||
and: jest.fn(),
|
||||
const mockWhere = vi.fn()
|
||||
const mockOrderBy = vi.fn()
|
||||
const mockLimit = vi.fn()
|
||||
vi.mock('firebase/firestore', () => ({
|
||||
collection: vi.fn(),
|
||||
query: vi.fn(),
|
||||
and: vi.fn(),
|
||||
where: (path, op, value) => mockWhere(path, op, value),
|
||||
limit: (limit) => mockLimit(limit),
|
||||
orderBy: (field, direction) => mockOrderBy(field, direction),
|
||||
}))
|
||||
|
||||
jest.mock('../../stores/databaseV2/endpoints', () => ({
|
||||
vi.mock('../../stores/databaseV2/endpoints', () => ({
|
||||
DB_ENDPOINTS: {
|
||||
questions: 'questions',
|
||||
questionCategories: 'questionCategories',
|
||||
},
|
||||
}))
|
||||
|
||||
jest.mock('../../config/config', () => ({
|
||||
getConfigurationOption: jest.fn(),
|
||||
vi.mock('../../config/config', () => ({
|
||||
getConfigurationOption: vi.fn(),
|
||||
FIREBASE_CONFIG: {
|
||||
apiKey: 'AIyChVN',
|
||||
databaseURL: 'https://test.firebaseio.com',
|
||||
projectId: 'test',
|
||||
storageBucket: 'test.appspot.com',
|
||||
},
|
||||
localStorage: jest.fn(),
|
||||
localStorage: vi.fn(),
|
||||
SITE: 'unit-tests',
|
||||
}))
|
||||
|
||||
describe('question.search', () => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { ResearchProvider } from 'src/test/components'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { ResearchErrors } from '.'
|
||||
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import { act } from 'react-dom/test-utils'
|
||||
import '@testing-library/jest-dom/vitest'
|
||||
|
||||
import { MemoryRouter } from 'react-router-dom'
|
||||
import { ThemeProvider } from '@emotion/react'
|
||||
import { fireEvent, render } from '@testing-library/react'
|
||||
import { render } from '@testing-library/react'
|
||||
import { FactoryResearchItemUpdate } from 'src/test/factories/ResearchItem'
|
||||
import { testingThemeStyles } from 'src/test/utils/themeUtils'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { ResearchUpdateForm } from './ResearchUpdate.form'
|
||||
|
||||
const Theme = testingThemeStyles
|
||||
|
||||
jest.mock('src/stores/Research/research.store', () => {
|
||||
vi.mock('src/stores/Research/research.store', () => {
|
||||
return {
|
||||
useResearchStore: () => ({
|
||||
updateUploadStatus: {
|
||||
@@ -19,8 +21,8 @@ jest.mock('src/stores/Research/research.store', () => {
|
||||
Database: false,
|
||||
Complete: false,
|
||||
},
|
||||
isTitleThatReusesSlug: jest.fn(),
|
||||
unlockResearchUpdate: jest.fn(),
|
||||
isTitleThatReusesSlug: vi.fn(),
|
||||
unlockResearchUpdate: vi.fn(),
|
||||
}),
|
||||
}
|
||||
})
|
||||
@@ -32,10 +34,7 @@ describe('Research update form', () => {
|
||||
fileLink: 'www.filedonwload.test',
|
||||
})
|
||||
|
||||
let wrapper
|
||||
await act(async () => {
|
||||
wrapper = await getWrapper(formValues, 'create', {})
|
||||
})
|
||||
const wrapper = getWrapper(formValues, 'create', {})
|
||||
|
||||
expect(
|
||||
wrapper.queryByTestId('invalid-file-warning'),
|
||||
@@ -49,41 +48,17 @@ describe('Research update form', () => {
|
||||
})
|
||||
|
||||
// Act
|
||||
let wrapper
|
||||
await act(async () => {
|
||||
wrapper = await getWrapper(formValues, 'create', {})
|
||||
})
|
||||
const wrapper = getWrapper(formValues, 'create', {})
|
||||
|
||||
// Assert
|
||||
expect(
|
||||
wrapper.queryByTestId('invalid-file-warning'),
|
||||
).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('Appears when submitting 2 file types', async () => {
|
||||
// Arrange
|
||||
const formValues = FactoryResearchItemUpdate({
|
||||
images: [new File(['hello'], 'hello.png')],
|
||||
files: [new File(['test file content'], 'test-file.zip')],
|
||||
fileLink: 'www.filedownload.test',
|
||||
})
|
||||
|
||||
// Act
|
||||
let wrapper
|
||||
await act(async () => {
|
||||
wrapper = await getWrapper(formValues, 'create', {})
|
||||
// submit form
|
||||
const submitFormButton = wrapper.getByTestId('submit-form')
|
||||
fireEvent.click(submitFormButton)
|
||||
})
|
||||
|
||||
// Assert
|
||||
expect(wrapper.queryByTestId('invalid-file-warning')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
const getWrapper = async (formValues, parentType, navProps) => {
|
||||
const getWrapper = (formValues, parentType, navProps) => {
|
||||
return render(
|
||||
<ThemeProvider theme={Theme}>
|
||||
<MemoryRouter initialEntries={['/research/:slug/update']}>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import '@testing-library/jest-dom/vitest'
|
||||
|
||||
import {
|
||||
createMemoryRouter,
|
||||
createRoutesFromElements,
|
||||
@@ -18,9 +20,12 @@ import {
|
||||
import { FactoryUser } from 'src/test/factories/User'
|
||||
import { testingThemeStyles } from 'src/test/utils/themeUtils'
|
||||
import { formatDate } from 'src/utils/date'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import ResearchArticle from './ResearchArticle'
|
||||
|
||||
import type { Mock } from 'vitest'
|
||||
|
||||
const Theme = testingThemeStyles
|
||||
|
||||
const activeUser = FactoryUser({
|
||||
@@ -30,21 +35,21 @@ const activeUser = FactoryUser({
|
||||
const mockUser = FactoryUser({ country: 'AF' })
|
||||
const mockDiscussionItem = FactoryDiscussion()
|
||||
|
||||
jest.mock('src/common/hooks/useCommonStores', () => ({
|
||||
vi.mock('src/common/hooks/useCommonStores', () => ({
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
__esModule: true,
|
||||
useCommonStores: () => ({
|
||||
stores: {
|
||||
userStore: {
|
||||
getUserByUsername: jest.fn().mockResolvedValue(mockUser),
|
||||
getUserByUsername: vi.fn().mockResolvedValue(mockUser),
|
||||
},
|
||||
aggregationsStore: {
|
||||
isVerified: jest.fn(),
|
||||
isVerified: vi.fn(),
|
||||
users_verified: {},
|
||||
},
|
||||
tagsStore: {},
|
||||
discussionStore: {
|
||||
fetchOrCreateDiscussionBySource: jest.fn().mockResolvedValue({
|
||||
fetchOrCreateDiscussionBySource: vi.fn().mockResolvedValue({
|
||||
mockDiscussionItem,
|
||||
}),
|
||||
activeUser: mockUser,
|
||||
@@ -53,16 +58,16 @@ jest.mock('src/common/hooks/useCommonStores', () => ({
|
||||
}),
|
||||
}))
|
||||
|
||||
jest.mock('src/stores/Research/research.store')
|
||||
vi.mock('src/stores/Research/research.store')
|
||||
|
||||
describe('Research Article', () => {
|
||||
const mockResearchStore = {
|
||||
activeResearchItem: FactoryResearchItem(),
|
||||
setActiveResearchItemBySlug: jest.fn().mockResolvedValue(true),
|
||||
addSubscriberToResearchArticle: jest.fn(),
|
||||
needsModeration: jest.fn(),
|
||||
formatResearchCommentList: jest.fn(),
|
||||
incrementViewCount: jest.fn(),
|
||||
setActiveResearchItemBySlug: vi.fn().mockResolvedValue(true),
|
||||
addSubscriberToResearchArticle: vi.fn(),
|
||||
needsModeration: vi.fn(),
|
||||
formatResearchCommentList: vi.fn(),
|
||||
incrementViewCount: vi.fn(),
|
||||
}
|
||||
|
||||
it('displays content statistics', async () => {
|
||||
@@ -77,7 +82,7 @@ describe('Research Article', () => {
|
||||
],
|
||||
})
|
||||
|
||||
;(useResearchStore as jest.Mock).mockReturnValue({
|
||||
;(useResearchStore as Mock).mockReturnValue({
|
||||
...mockResearchStore,
|
||||
activeResearchItem,
|
||||
})
|
||||
@@ -100,7 +105,7 @@ describe('Research Article', () => {
|
||||
|
||||
it('does not display contributors when undefined', async () => {
|
||||
// Arrange
|
||||
;(useResearchStore as jest.Mock).mockReturnValue({
|
||||
;(useResearchStore as Mock).mockReturnValue({
|
||||
...mockResearchStore,
|
||||
activeResearchItem: FactoryResearchItem({
|
||||
collaborators: undefined,
|
||||
@@ -121,7 +126,7 @@ describe('Research Article', () => {
|
||||
|
||||
it('displays contributors', async () => {
|
||||
// Arrange
|
||||
;(useResearchStore as jest.Mock).mockReturnValue({
|
||||
;(useResearchStore as Mock).mockReturnValue({
|
||||
...mockResearchStore,
|
||||
activeResearchItem: FactoryResearchItem({
|
||||
collaborators: ['example-username', 'another-example-username'],
|
||||
@@ -143,7 +148,7 @@ describe('Research Article', () => {
|
||||
|
||||
it('displays "Follow" button for non-subscriber', async () => {
|
||||
// Arrange
|
||||
;(useResearchStore as jest.Mock).mockReturnValue({
|
||||
;(useResearchStore as Mock).mockReturnValue({
|
||||
...mockResearchStore,
|
||||
activeResearchItem: FactoryResearchItem({
|
||||
userHasSubscribed: false,
|
||||
@@ -169,7 +174,7 @@ describe('Research Article', () => {
|
||||
// TODO: Work out how to simulate store subscribe functionality
|
||||
// it('displays "Following" button for subscriber', async () => {
|
||||
// // Arrange
|
||||
// ;(useResearchStore as jest.Mock).mockReturnValue({
|
||||
// ;(useResearchStore as Mock).mockReturnValue({
|
||||
// ...mockResearchStore,
|
||||
// activeResearchItem: FactoryResearchItem({
|
||||
// subscribers: [activeUser._id],
|
||||
@@ -192,7 +197,7 @@ describe('Research Article', () => {
|
||||
describe('Research Update', () => {
|
||||
it('displays contributors', async () => {
|
||||
// Arrange
|
||||
;(useResearchStore as jest.Mock).mockReturnValue({
|
||||
;(useResearchStore as Mock).mockReturnValue({
|
||||
...mockResearchStore,
|
||||
activeResearchItem: FactoryResearchItem({
|
||||
collaborators: ['example-username', 'another-example-username'],
|
||||
@@ -248,9 +253,9 @@ describe('Research Article', () => {
|
||||
title: 'A title',
|
||||
description: 'A description',
|
||||
})
|
||||
;(useResearchStore as jest.Mock).mockReturnValue({
|
||||
;(useResearchStore as Mock).mockReturnValue({
|
||||
...mockResearchStore,
|
||||
formatResearchCommentList: jest.fn().mockImplementation((c) => {
|
||||
formatResearchCommentList: vi.fn().mockImplementation((c) => {
|
||||
return c
|
||||
}),
|
||||
activeResearchItem: FactoryResearchItem({
|
||||
@@ -279,9 +284,9 @@ describe('Research Article', () => {
|
||||
description: 'A description',
|
||||
_deleted: false,
|
||||
})
|
||||
;(useResearchStore as jest.Mock).mockReturnValue({
|
||||
;(useResearchStore as Mock).mockReturnValue({
|
||||
...mockResearchStore,
|
||||
formatResearchCommentList: jest.fn().mockImplementation((c) => {
|
||||
formatResearchCommentList: vi.fn().mockImplementation((c) => {
|
||||
return c
|
||||
}),
|
||||
activeResearchItem: FactoryResearchItem({
|
||||
@@ -303,7 +308,7 @@ describe('Research Article', () => {
|
||||
|
||||
it('shows only published updates', async () => {
|
||||
// Arrange
|
||||
;(useResearchStore as jest.Mock).mockReturnValue({
|
||||
;(useResearchStore as Mock).mockReturnValue({
|
||||
...mockResearchStore,
|
||||
activeResearchItem: FactoryResearchItem({
|
||||
collaborators: ['example-username', 'another-example-username'],
|
||||
@@ -336,7 +341,7 @@ describe('Research Article', () => {
|
||||
describe('Breadcrumbs', () => {
|
||||
it('displays breadcrumbs with category', async () => {
|
||||
// Arrange
|
||||
;(useResearchStore as jest.Mock).mockReturnValue({
|
||||
;(useResearchStore as Mock).mockReturnValue({
|
||||
...mockResearchStore,
|
||||
activeResearchItem: FactoryResearchItem({
|
||||
title: 'Innovative Study',
|
||||
@@ -377,7 +382,7 @@ describe('Research Article', () => {
|
||||
|
||||
it('displays breadcrumbs without category', async () => {
|
||||
// Arrange
|
||||
;(useResearchStore as jest.Mock).mockReturnValue({
|
||||
;(useResearchStore as Mock).mockReturnValue({
|
||||
...mockResearchStore,
|
||||
activeResearchItem: FactoryResearchItem({
|
||||
title: 'Innovative Study',
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { getResearchCommentId } from './helper'
|
||||
|
||||
describe('getReseachCommentId', () => {
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
} from 'react-router-dom'
|
||||
import { ThemeProvider } from '@emotion/react'
|
||||
import { faker } from '@faker-js/faker'
|
||||
import { act, cleanup, render, waitFor } from '@testing-library/react'
|
||||
import { act, cleanup, render } from '@testing-library/react'
|
||||
import { Provider } from 'mobx-react'
|
||||
import { UserRole } from 'oa-shared'
|
||||
import { useResearchStore } from 'src/stores/Research/research.store'
|
||||
@@ -19,30 +19,32 @@ import {
|
||||
} from 'src/test/factories/ResearchItem'
|
||||
import { FactoryUser } from 'src/test/factories/User'
|
||||
import { testingThemeStyles } from 'src/test/utils/themeUtils'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { researchRouteElements } from './research.routes'
|
||||
import { researchService } from './research.service'
|
||||
|
||||
import type { ResearchStore } from 'src/stores/Research/research.store'
|
||||
import type { Mock } from 'vitest'
|
||||
|
||||
const Theme = testingThemeStyles
|
||||
const mockActiveUser = FactoryUser()
|
||||
|
||||
jest.mock('src/pages/Research/research.service')
|
||||
vi.mock('src/pages/Research/research.service')
|
||||
|
||||
// Similar to issues in Academy.test.tsx - stub methods called in user store constructor
|
||||
// TODO - replace with mock store or avoid direct call
|
||||
jest.mock('src/common/hooks/useCommonStores', () => ({
|
||||
vi.mock('src/common/hooks/useCommonStores', () => ({
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
__esModule: true,
|
||||
useCommonStores: () => ({
|
||||
stores: {
|
||||
userStore: {
|
||||
fetchAllVerifiedUsers: jest.fn(),
|
||||
fetchAllVerifiedUsers: vi.fn(),
|
||||
user: mockActiveUser,
|
||||
},
|
||||
aggregationsStore: {
|
||||
isVerified: jest.fn(),
|
||||
isVerified: vi.fn(),
|
||||
users_verified: {
|
||||
HowtoAuthor: true,
|
||||
},
|
||||
@@ -55,17 +57,17 @@ jest.mock('src/common/hooks/useCommonStores', () => ({
|
||||
}),
|
||||
}))
|
||||
|
||||
const mockedUsedNavigate = jest.fn()
|
||||
jest.mock('react-router-dom', () => ({
|
||||
...(jest.requireActual('react-router-dom') as any),
|
||||
const mockedUsedNavigate = vi.fn()
|
||||
vi.mock('react-router-dom', async () => ({
|
||||
...((await vi.importActual('react-router-dom')) as any),
|
||||
useNavigate: () => mockedUsedNavigate,
|
||||
}))
|
||||
|
||||
/** When mocking research routes replace default store methods with below */
|
||||
class MockResearchStoreClass implements Partial<ResearchStore> {
|
||||
setActiveResearchItemBySlug = jest.fn()
|
||||
needsModeration = jest.fn().mockResolvedValue(true)
|
||||
incrementViewCount = jest.fn()
|
||||
setActiveResearchItemBySlug = vi.fn()
|
||||
needsModeration = vi.fn().mockResolvedValue(true)
|
||||
incrementViewCount = vi.fn()
|
||||
activeResearchItem = FactoryResearchItem({
|
||||
title: 'Research article title',
|
||||
updates: [],
|
||||
@@ -73,12 +75,12 @@ class MockResearchStoreClass implements Partial<ResearchStore> {
|
||||
})
|
||||
researchUploadStatus = {} as any
|
||||
updateUploadStatus = {} as any
|
||||
formatResearchCommentList = jest.fn()
|
||||
getActiveResearchUpdateComments = jest.fn()
|
||||
lockResearchItem = jest.fn()
|
||||
lockResearchUpdate = jest.fn()
|
||||
unlockResearchUpdate = jest.fn()
|
||||
unlockResearchItem = jest.fn()
|
||||
formatResearchCommentList = vi.fn()
|
||||
getActiveResearchUpdateComments = vi.fn()
|
||||
lockResearchItem = vi.fn()
|
||||
lockResearchUpdate = vi.fn()
|
||||
unlockResearchUpdate = vi.fn()
|
||||
unlockResearchItem = vi.fn()
|
||||
|
||||
get activeUser() {
|
||||
return {
|
||||
@@ -93,26 +95,24 @@ class MockResearchStoreClass implements Partial<ResearchStore> {
|
||||
}
|
||||
const mockResearchStore = new MockResearchStoreClass()
|
||||
|
||||
jest.mock('src/stores/Research/research.store')
|
||||
vi.mock('src/stores/Research/research.store')
|
||||
|
||||
describe('research.routes', () => {
|
||||
beforeEach(() => {
|
||||
;(useResearchStore as jest.Mock).mockReturnValue(mockResearchStore)
|
||||
;(useResearchStore as Mock).mockReturnValue(mockResearchStore)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks()
|
||||
vi.restoreAllMocks()
|
||||
cleanup()
|
||||
})
|
||||
|
||||
describe('/research/', () => {
|
||||
it('renders the research listing', async () => {
|
||||
let wrapper
|
||||
|
||||
const researchTitle = faker.lorem.words(3)
|
||||
const researchSlug = faker.lorem.slug()
|
||||
|
||||
researchService.search = jest.fn(() => {
|
||||
researchService.search = vi.fn(() => {
|
||||
return new Promise((resolve) => {
|
||||
resolve({
|
||||
items: [
|
||||
@@ -130,11 +130,9 @@ describe('research.routes', () => {
|
||||
})
|
||||
})
|
||||
|
||||
await act(async () => {
|
||||
wrapper = renderFn('/research').wrapper
|
||||
})
|
||||
const wrapper = renderFn('/research')
|
||||
|
||||
await waitFor(
|
||||
await vi.waitFor(
|
||||
() =>
|
||||
expect(
|
||||
wrapper.getByText(/Help out with Research & Development/),
|
||||
@@ -149,11 +147,11 @@ describe('research.routes', () => {
|
||||
describe('/research/:slug', () => {
|
||||
it('renders an individual research article', async () => {
|
||||
let wrapper
|
||||
await act(async () => {
|
||||
wrapper = renderFn('/research/research-slug').wrapper
|
||||
act(() => {
|
||||
wrapper = renderFn('/research/research-slug')
|
||||
})
|
||||
|
||||
await waitFor(
|
||||
await vi.waitFor(
|
||||
() => {
|
||||
expect(
|
||||
mockResearchStore.setActiveResearchItemBySlug,
|
||||
@@ -173,11 +171,11 @@ describe('research.routes', () => {
|
||||
it('rejects a request without a user present', async () => {
|
||||
mockActiveUser.userRoles = []
|
||||
let wrapper
|
||||
await act(async () => {
|
||||
wrapper = renderFn('/research/create').wrapper
|
||||
act(() => {
|
||||
wrapper = renderFn('/research/create')
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
await vi.waitFor(() => {
|
||||
expect(
|
||||
wrapper.getByText(/role required to access this page/),
|
||||
).toBeInTheDocument()
|
||||
@@ -186,11 +184,11 @@ describe('research.routes', () => {
|
||||
|
||||
it('rejects a logged in user missing required role', async () => {
|
||||
let wrapper
|
||||
await act(async () => {
|
||||
wrapper = renderFn('/research/create').wrapper
|
||||
act(() => {
|
||||
wrapper = renderFn('/research/create')
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
await vi.waitFor(() => {
|
||||
expect(
|
||||
wrapper.getByText(/role required to access this page/),
|
||||
).toBeInTheDocument()
|
||||
@@ -199,13 +197,13 @@ describe('research.routes', () => {
|
||||
|
||||
it('accepts a logged in user with required role [research_creator]', async () => {
|
||||
let wrapper
|
||||
await act(async () => {
|
||||
act(() => {
|
||||
mockActiveUser.userRoles = [UserRole.RESEARCH_CREATOR]
|
||||
|
||||
wrapper = renderFn('/research/create').wrapper
|
||||
wrapper = renderFn('/research/create')
|
||||
})
|
||||
|
||||
await waitFor(
|
||||
await vi.waitFor(
|
||||
() => {
|
||||
expect(wrapper.getByText(/start your research/i)).toBeInTheDocument()
|
||||
},
|
||||
@@ -217,12 +215,12 @@ describe('research.routes', () => {
|
||||
|
||||
it('accepts a logged in user with required role [research_creator]', async () => {
|
||||
let wrapper
|
||||
await act(async () => {
|
||||
act(() => {
|
||||
mockActiveUser.userRoles = [UserRole.RESEARCH_EDITOR]
|
||||
|
||||
wrapper = renderFn('/research/create').wrapper
|
||||
wrapper = renderFn('/research/create')
|
||||
})
|
||||
await waitFor(
|
||||
await vi.waitFor(
|
||||
() => {
|
||||
expect(wrapper.getByText(/start your research/i)).toBeInTheDocument()
|
||||
},
|
||||
@@ -238,11 +236,11 @@ describe('research.routes', () => {
|
||||
mockActiveUser.userRoles = []
|
||||
|
||||
let wrapper
|
||||
await act(async () => {
|
||||
wrapper = renderFn('/research/an-example/edit').wrapper
|
||||
act(() => {
|
||||
wrapper = renderFn('/research/an-example/edit')
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
await vi.waitFor(() => {
|
||||
expect(
|
||||
wrapper.getByText(/role required to access this page/),
|
||||
).toBeInTheDocument()
|
||||
@@ -251,14 +249,14 @@ describe('research.routes', () => {
|
||||
|
||||
it('accepts a logged in user with required role', async () => {
|
||||
let wrapper
|
||||
await act(async () => {
|
||||
act(() => {
|
||||
mockActiveUser.userName = 'Jaasper'
|
||||
mockActiveUser.userRoles = [UserRole.RESEARCH_EDITOR]
|
||||
|
||||
wrapper = renderFn('/research/an-example/edit').wrapper
|
||||
wrapper = renderFn('/research/an-example/edit')
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
await vi.waitFor(() => {
|
||||
expect(wrapper.getByText(/edit your research/i)).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
@@ -267,7 +265,7 @@ describe('research.routes', () => {
|
||||
mockActiveUser.userRoles = [UserRole.RESEARCH_EDITOR]
|
||||
|
||||
// Arrange
|
||||
;(useResearchStore as jest.Mock).mockReturnValue({
|
||||
;(useResearchStore as Mock).mockReturnValue({
|
||||
...mockResearchStore,
|
||||
activeUser: mockActiveUser,
|
||||
activeResearchItem: FactoryResearchItem({
|
||||
@@ -276,18 +274,18 @@ describe('research.routes', () => {
|
||||
}),
|
||||
})
|
||||
|
||||
await act(async () => {
|
||||
act(() => {
|
||||
renderFn('/research/an-example/edit')
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
await vi.waitFor(() => {
|
||||
expect(mockedUsedNavigate).toHaveBeenCalledWith('/research/an-example')
|
||||
})
|
||||
})
|
||||
|
||||
it('blocks a valid editor when document is locked by another user', async () => {
|
||||
mockActiveUser.userRoles = [UserRole.RESEARCH_EDITOR]
|
||||
;(useResearchStore as jest.Mock).mockReturnValue({
|
||||
;(useResearchStore as Mock).mockReturnValue({
|
||||
...mockResearchStore,
|
||||
activeUser: mockActiveUser,
|
||||
activeResearchItem: FactoryResearchItem({
|
||||
@@ -301,11 +299,11 @@ describe('research.routes', () => {
|
||||
})
|
||||
|
||||
let wrapper
|
||||
await act(async () => {
|
||||
wrapper = renderFn('/research/an-example/edit').wrapper
|
||||
act(() => {
|
||||
wrapper = renderFn('/research/an-example/edit')
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
await vi.waitFor(() => {
|
||||
expect(
|
||||
wrapper.getByText(
|
||||
'The research description is currently being edited by another editor.',
|
||||
@@ -316,7 +314,7 @@ describe('research.routes', () => {
|
||||
|
||||
it('accepts a user when document is mark locked by them', async () => {
|
||||
mockActiveUser.userRoles = [UserRole.RESEARCH_EDITOR]
|
||||
;(useResearchStore as jest.Mock).mockReturnValue({
|
||||
;(useResearchStore as Mock).mockReturnValue({
|
||||
...mockResearchStore,
|
||||
activeUser: mockActiveUser,
|
||||
activeResearchItem: FactoryResearchItem({
|
||||
@@ -330,18 +328,18 @@ describe('research.routes', () => {
|
||||
})
|
||||
|
||||
let wrapper
|
||||
await act(async () => {
|
||||
wrapper = renderFn('/research/an-example/edit').wrapper
|
||||
act(() => {
|
||||
wrapper = renderFn('/research/an-example/edit')
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
await vi.waitFor(() => {
|
||||
expect(wrapper.getByText('Edit your Research')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
it('accepts a user with required role and contributor acccess', async () => {
|
||||
mockActiveUser.userRoles = [UserRole.RESEARCH_EDITOR]
|
||||
;(useResearchStore as jest.Mock).mockReturnValue({
|
||||
;(useResearchStore as Mock).mockReturnValue({
|
||||
...mockResearchStore,
|
||||
activeUser: mockActiveUser,
|
||||
activeResearchItem: FactoryResearchItem({
|
||||
@@ -351,11 +349,11 @@ describe('research.routes', () => {
|
||||
})
|
||||
|
||||
let wrapper
|
||||
await act(async () => {
|
||||
wrapper = renderFn('/research/an-example/edit').wrapper
|
||||
act(() => {
|
||||
wrapper = renderFn('/research/an-example/edit')
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
await vi.waitFor(() => {
|
||||
expect(wrapper.getByText(/edit your research/i)).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
@@ -366,11 +364,11 @@ describe('research.routes', () => {
|
||||
mockActiveUser.userRoles = []
|
||||
|
||||
let wrapper
|
||||
await act(async () => {
|
||||
wrapper = renderFn('/research/an-example/new-update').wrapper
|
||||
act(() => {
|
||||
wrapper = renderFn('/research/an-example/new-update')
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
await vi.waitFor(() => {
|
||||
expect(
|
||||
wrapper.getByText(/role required to access this page/),
|
||||
).toBeInTheDocument()
|
||||
@@ -379,12 +377,12 @@ describe('research.routes', () => {
|
||||
|
||||
it('accepts a logged in user with required role', async () => {
|
||||
let wrapper
|
||||
await act(async () => {
|
||||
act(() => {
|
||||
mockActiveUser.userRoles = [UserRole.RESEARCH_EDITOR]
|
||||
|
||||
wrapper = renderFn('/research/an-example/new-update').wrapper
|
||||
wrapper = renderFn('/research/an-example/new-update')
|
||||
})
|
||||
await waitFor(() => {
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(wrapper.getByTestId('EditResearchUpdate')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
@@ -393,14 +391,11 @@ describe('research.routes', () => {
|
||||
describe('/research/:slug/edit-update/:id', () => {
|
||||
it('rejects a request without a user present', async () => {
|
||||
mockActiveUser.userRoles = []
|
||||
let wrapper
|
||||
await act(async () => {
|
||||
wrapper = renderFn(
|
||||
'/research/an-example/edit-update/nested-research-update',
|
||||
).wrapper
|
||||
})
|
||||
const wrapper = renderFn(
|
||||
'/research/an-example/edit-update/nested-research-update',
|
||||
)
|
||||
|
||||
await waitFor(() => {
|
||||
await vi.waitFor(() => {
|
||||
expect(
|
||||
wrapper.getByText(/role required to access this page/),
|
||||
).toBeInTheDocument()
|
||||
@@ -410,7 +405,7 @@ describe('research.routes', () => {
|
||||
it('accept logged in author present', async () => {
|
||||
mockActiveUser.userRoles = [UserRole.RESEARCH_EDITOR]
|
||||
// Arrange
|
||||
;(useResearchStore as jest.Mock).mockReturnValue({
|
||||
;(useResearchStore as Mock).mockReturnValue({
|
||||
...mockResearchStore,
|
||||
activeUser: mockActiveUser,
|
||||
activeResearchItem: FactoryResearchItem({
|
||||
@@ -426,13 +421,13 @@ describe('research.routes', () => {
|
||||
})
|
||||
|
||||
let wrapper
|
||||
await act(async () => {
|
||||
act(() => {
|
||||
wrapper = renderFn(
|
||||
'/research/an-example/edit-update/nested-research-update',
|
||||
).wrapper
|
||||
)
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
await vi.waitFor(() => {
|
||||
expect(wrapper.getByTestId(/EditResearchUpdate/i)).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
@@ -440,7 +435,7 @@ describe('research.routes', () => {
|
||||
it('blocks valid author when document is locked', async () => {
|
||||
// Arrange
|
||||
mockActiveUser.userRoles = [UserRole.RESEARCH_EDITOR]
|
||||
;(useResearchStore as jest.Mock).mockReturnValue({
|
||||
;(useResearchStore as Mock).mockReturnValue({
|
||||
...mockResearchStore,
|
||||
activeUser: mockActiveUser,
|
||||
activeResearchItem: FactoryResearchItem({
|
||||
@@ -459,11 +454,11 @@ describe('research.routes', () => {
|
||||
}),
|
||||
})
|
||||
|
||||
const { wrapper } = renderFn(
|
||||
const wrapper = renderFn(
|
||||
'/research/an-example/edit-update/nested-research-update',
|
||||
)
|
||||
|
||||
await waitFor(() => {
|
||||
await vi.waitFor(() => {
|
||||
expect(
|
||||
wrapper.getByText(
|
||||
/This research update is currently being edited by another editor/,
|
||||
@@ -474,7 +469,7 @@ describe('research.routes', () => {
|
||||
|
||||
it('accepts a user when document is mark locked by them', async () => {
|
||||
mockActiveUser.userRoles = [UserRole.RESEARCH_EDITOR]
|
||||
;(useResearchStore as jest.Mock).mockReturnValue({
|
||||
;(useResearchStore as Mock).mockReturnValue({
|
||||
...mockResearchStore,
|
||||
activeUser: mockActiveUser,
|
||||
activeResearchItem: FactoryResearchItem({
|
||||
@@ -492,11 +487,11 @@ describe('research.routes', () => {
|
||||
}),
|
||||
})
|
||||
|
||||
const { wrapper } = renderFn(
|
||||
const wrapper = renderFn(
|
||||
'/research/an-example/edit-update/nested-research-update',
|
||||
)
|
||||
|
||||
await waitFor(() => {
|
||||
await vi.waitFor(() => {
|
||||
expect(wrapper.getByText('Edit your update')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
@@ -505,13 +500,13 @@ describe('research.routes', () => {
|
||||
mockActiveUser.userRoles = []
|
||||
|
||||
let wrapper
|
||||
await act(async () => {
|
||||
act(() => {
|
||||
wrapper = renderFn(
|
||||
'/research/an-example/edit-update/nested-research-update',
|
||||
).wrapper
|
||||
)
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
await vi.waitFor(() => {
|
||||
expect(
|
||||
wrapper.getByText(/role required to access this page/),
|
||||
).toBeInTheDocument()
|
||||
@@ -522,7 +517,7 @@ describe('research.routes', () => {
|
||||
mockActiveUser.userRoles = [UserRole.RESEARCH_EDITOR]
|
||||
|
||||
// Arrange
|
||||
;(useResearchStore as jest.Mock).mockReturnValue({
|
||||
;(useResearchStore as Mock).mockReturnValue({
|
||||
...mockResearchStore,
|
||||
activeUser: mockActiveUser,
|
||||
activeResearchItem: FactoryResearchItem({
|
||||
@@ -537,13 +532,13 @@ describe('research.routes', () => {
|
||||
})
|
||||
|
||||
let wrapper
|
||||
await act(async () => {
|
||||
act(() => {
|
||||
wrapper = renderFn(
|
||||
'/research/an-example/edit-update/nested-research-update',
|
||||
).wrapper
|
||||
)
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
await vi.waitFor(() => {
|
||||
expect(wrapper.getByTestId(/EditResearchUpdate/i)).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
@@ -560,15 +555,13 @@ const renderFn = (url: string) => {
|
||||
},
|
||||
)
|
||||
|
||||
return {
|
||||
wrapper: render(
|
||||
<Suspense fallback={<></>}>
|
||||
<Provider userStore={{ user: mockActiveUser }} tagsStore={{}}>
|
||||
<ThemeProvider theme={Theme}>
|
||||
<RouterProvider router={router} />
|
||||
</ThemeProvider>
|
||||
</Provider>
|
||||
</Suspense>,
|
||||
),
|
||||
}
|
||||
return render(
|
||||
<Suspense fallback={<></>}>
|
||||
<Provider userStore={{ user: mockActiveUser }} tagsStore={{}}>
|
||||
<ThemeProvider theme={Theme}>
|
||||
<RouterProvider router={router} />
|
||||
</ThemeProvider>
|
||||
</Provider>
|
||||
</Suspense>,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,37 +1,39 @@
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
import { ResearchStatus } from 'oa-shared'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { exportedForTesting } from './research.service'
|
||||
|
||||
const mockWhere = jest.fn()
|
||||
const mockOrderBy = jest.fn()
|
||||
const mockLimit = jest.fn()
|
||||
jest.mock('firebase/firestore', () => ({
|
||||
collection: jest.fn(),
|
||||
query: jest.fn(),
|
||||
and: jest.fn(),
|
||||
const mockWhere = vi.fn()
|
||||
const mockOrderBy = vi.fn()
|
||||
const mockLimit = vi.fn()
|
||||
vi.mock('firebase/firestore', () => ({
|
||||
collection: vi.fn(),
|
||||
query: vi.fn(),
|
||||
and: vi.fn(),
|
||||
where: (path, op, value) => mockWhere(path, op, value),
|
||||
limit: (limit) => mockLimit(limit),
|
||||
orderBy: (field, direction) => mockOrderBy(field, direction),
|
||||
}))
|
||||
|
||||
jest.mock('../../stores/databaseV2/endpoints', () => ({
|
||||
vi.mock('../../stores/databaseV2/endpoints', () => ({
|
||||
DB_ENDPOINTS: {
|
||||
research: 'research',
|
||||
researchCategories: 'researchCategories',
|
||||
},
|
||||
}))
|
||||
|
||||
jest.mock('../../config/config', () => ({
|
||||
getConfigurationOption: jest.fn(),
|
||||
vi.mock('../../config/config', () => ({
|
||||
getConfigurationOption: vi.fn(),
|
||||
FIREBASE_CONFIG: {
|
||||
apiKey: 'AIyChVN',
|
||||
databaseURL: 'https://test.firebaseio.com',
|
||||
projectId: 'test',
|
||||
storageBucket: 'test.appspot.com',
|
||||
},
|
||||
localStorage: jest.fn(),
|
||||
localStorage: vi.fn(),
|
||||
SITE: 'unit-tests',
|
||||
}))
|
||||
|
||||
describe('research.search', () => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
jest.mock('../../stores/common/module.store')
|
||||
import '@testing-library/jest-dom/vitest'
|
||||
|
||||
import {
|
||||
createMemoryRouter,
|
||||
@@ -10,16 +10,19 @@ import { ThemeProvider } from '@emotion/react'
|
||||
import { render, waitFor } from '@testing-library/react'
|
||||
import { Provider } from 'mobx-react'
|
||||
import { testingThemeStyles } from 'src/test/utils/themeUtils'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import Unsubscribe from './Unsubscribe'
|
||||
|
||||
vi.mock('../../stores/common/module.store')
|
||||
|
||||
const Theme = testingThemeStyles
|
||||
|
||||
const mockUnsubscribeUser = jest.fn()
|
||||
const mockUnsubscribeUser = vi.fn()
|
||||
|
||||
// Similar to issues in Academy.test.tsx - stub methods called in user store constructor
|
||||
// TODO - replace with mock store or avoid direct call
|
||||
jest.mock('src/common/hooks/useCommonStores', () => ({
|
||||
vi.mock('src/common/hooks/useCommonStores', () => ({
|
||||
__esModule: true,
|
||||
useCommonStores: () => ({
|
||||
stores: {
|
||||
@@ -28,7 +31,7 @@ jest.mock('src/common/hooks/useCommonStores', () => ({
|
||||
unsubscribeUser: mockUnsubscribeUser,
|
||||
},
|
||||
aggregationsStore: {
|
||||
isVerified: jest.fn(),
|
||||
isVerified: vi.fn(),
|
||||
users_verified: {
|
||||
HowtoAuthor: true,
|
||||
},
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user