LFFE-14 Merge branch 'feature/LFFE-14_testing_config' into 'develop'
This commit is contained in:
81
.gitlab-ci.yml
Normal file
81
.gitlab-ci.yml
Normal file
@@ -0,0 +1,81 @@
|
||||
image: node:latest
|
||||
|
||||
cache:
|
||||
paths:
|
||||
- node_modules
|
||||
|
||||
stages:
|
||||
- lint
|
||||
- test
|
||||
- test:build
|
||||
- deploy
|
||||
|
||||
merge:test:lint:
|
||||
stage: lint
|
||||
script:
|
||||
- npm install
|
||||
- cp example.env.local .env.local
|
||||
- cp example.env.local .env.test
|
||||
- npm run lint
|
||||
only:
|
||||
- merge_requests
|
||||
|
||||
merge:test:jest:
|
||||
stage: test
|
||||
script:
|
||||
- npm install
|
||||
- cp example.env.local .env.local
|
||||
- cp example.env.local .env.test
|
||||
- npm run test
|
||||
only:
|
||||
- merge_requests
|
||||
|
||||
#merge:test:build:
|
||||
# stage: test:build
|
||||
# script:
|
||||
# - npm install
|
||||
# - cp example.env.local .env.local
|
||||
# - cp example.env.local .env.test
|
||||
# - npm run build
|
||||
# only:
|
||||
# - merge_requests
|
||||
|
||||
test:lint:
|
||||
stage: lint
|
||||
script:
|
||||
- npm install
|
||||
- cp example.env.local .env.local
|
||||
- cp example.env.local .env.test
|
||||
- npm run lint
|
||||
only:
|
||||
- develop
|
||||
- main
|
||||
|
||||
test:jest:
|
||||
stage: test
|
||||
script:
|
||||
- npm install
|
||||
- cp example.env.local .env.local
|
||||
- cp example.env.local .env.test
|
||||
- npm run test
|
||||
only:
|
||||
- develop
|
||||
- main
|
||||
|
||||
#test:build:
|
||||
# stage: test:build
|
||||
# script:
|
||||
# - npm install
|
||||
# - cp example.env.local .env.local
|
||||
# - cp example.env.local .env.test
|
||||
# - npm run build
|
||||
# only:
|
||||
# - develop
|
||||
# - main
|
||||
|
||||
deploy:
|
||||
stage: deploy
|
||||
script:
|
||||
- echo "Run deploy project"
|
||||
only:
|
||||
- main
|
||||
16
cypress.config.js
Normal file
16
cypress.config.js
Normal file
@@ -0,0 +1,16 @@
|
||||
const { defineConfig } = require("cypress");
|
||||
|
||||
module.exports = defineConfig({
|
||||
e2e: {
|
||||
setupNodeEvents(on, config) {
|
||||
// implement node event listeners here
|
||||
},
|
||||
},
|
||||
|
||||
component: {
|
||||
devServer: {
|
||||
framework: "next",
|
||||
bundler: "webpack",
|
||||
},
|
||||
},
|
||||
});
|
||||
25
cypress/support/commands.js
Normal file
25
cypress/support/commands.js
Normal file
@@ -0,0 +1,25 @@
|
||||
// ***********************************************
|
||||
// This example commands.js shows you how to
|
||||
// create various custom commands and overwrite
|
||||
// existing commands.
|
||||
//
|
||||
// For more comprehensive examples of custom
|
||||
// commands please read more here:
|
||||
// https://on.cypress.io/custom-commands
|
||||
// ***********************************************
|
||||
//
|
||||
//
|
||||
// -- This is a parent command --
|
||||
// Cypress.Commands.add('login', (email, password) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a child command --
|
||||
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a dual command --
|
||||
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
|
||||
14
cypress/support/component-index.html
Normal file
14
cypress/support/component-index.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<title>Components App</title>
|
||||
<!-- Used by Next.js to inject CSS. -->
|
||||
<div id="__next_css__DO_NOT_USE__"></div>
|
||||
</head>
|
||||
<body>
|
||||
<div data-cy-root></div>
|
||||
</body>
|
||||
</html>
|
||||
27
cypress/support/component.js
Normal file
27
cypress/support/component.js
Normal file
@@ -0,0 +1,27 @@
|
||||
// ***********************************************************
|
||||
// This example support/component.js is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import './commands'
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
||||
|
||||
import { mount } from 'cypress/react18'
|
||||
|
||||
Cypress.Commands.add('mount', mount)
|
||||
|
||||
// Example use:
|
||||
// cy.mount(<MyComponent />)
|
||||
20
cypress/support/e2e.js
Normal file
20
cypress/support/e2e.js
Normal file
@@ -0,0 +1,20 @@
|
||||
// ***********************************************************
|
||||
// This example support/e2e.js is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import './commands'
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
||||
@@ -7,6 +7,7 @@ NEXT_PUBLIC_PRIMARY_MAIN = "#084070"
|
||||
NEXT_PUBLIC_SECONDARY_MAIN = "#FF4E00"
|
||||
|
||||
NEXT_PUBLIC_BASE_URL = "https://loan.witel.ir"
|
||||
NEXT_PUBLIC_POWERED_BY_URL = "https://witel.ir"
|
||||
|
||||
#["NEXT_PUBLIC_HAS_WIDGET" , "NEXT_PUBLIC_HAS_NOTIFICATION"]
|
||||
NEXT_PUBLIC_HAS_VALUE=["NEXT_PUBLIC_HAS_NOTIFICATION"]
|
||||
|
||||
17
jest.config.mjs
Normal file
17
jest.config.mjs
Normal file
@@ -0,0 +1,17 @@
|
||||
import nextJest from 'next/jest.js'
|
||||
|
||||
const createJestConfig = nextJest({
|
||||
// Provide the path to your Next.js app to load next.config.js and .env files in your test environment
|
||||
dir: './',
|
||||
})
|
||||
|
||||
// Add any custom config to be passed to Jest
|
||||
/** @type {import('jest').Config} */
|
||||
const config = {
|
||||
// Add more setup options before each test is run
|
||||
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
|
||||
testEnvironment: 'jest-environment-jsdom',
|
||||
}
|
||||
|
||||
// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
|
||||
export default createJestConfig(config)
|
||||
22
jest.setup.js
Normal file
22
jest.setup.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import '@testing-library/jest-dom'
|
||||
import {server} from "./mocks/server";
|
||||
import mockRouter from 'next-router-mock'
|
||||
|
||||
jest.mock('next/router', () => jest.requireActual('next-router-mock'))
|
||||
|
||||
beforeAll(() => {
|
||||
server.listen()
|
||||
})
|
||||
beforeEach(() => {
|
||||
localStorage.clear();
|
||||
mockRouter.query = {}
|
||||
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
server.resetHandlers()
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
server.close()
|
||||
})
|
||||
13
mocks/AppWithProvider.jsx
Normal file
13
mocks/AppWithProvider.jsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import App from "@/pages/_app";
|
||||
import fa from "&/locales/fa/app.json"
|
||||
|
||||
const translations = {fa};
|
||||
const MockAppWithProviders = ({children, locale, title, isBot}) => {
|
||||
const pageProps = {
|
||||
title: title || '', isBot: isBot || true, locale: locale || 'fa', messages: translations[locale || 'fa']
|
||||
}
|
||||
|
||||
return (<App Component={() => (<>{children}</>)} pageProps={pageProps}/>)
|
||||
}
|
||||
|
||||
export default MockAppWithProviders
|
||||
5
mocks/handler.js
Normal file
5
mocks/handler.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import {userHandler} from "./handlers/user";
|
||||
|
||||
export const handler = [
|
||||
...userHandler
|
||||
];
|
||||
19
mocks/handlers/user.js
Normal file
19
mocks/handlers/user.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import {rest} from "msw";
|
||||
import {GET_USER_ROUTE} from "@/core/data/apiRoutes";
|
||||
|
||||
export const userHandler = [
|
||||
rest.get(GET_USER_ROUTE, (req, res, ctx) => {
|
||||
return res(ctx.json({
|
||||
data: {
|
||||
id: 10,
|
||||
full_name: "Witel Company",
|
||||
position: "Software Engineer",
|
||||
permissions: [
|
||||
"manage_users",
|
||||
"manage_roles",
|
||||
"manage_boss"
|
||||
],
|
||||
}
|
||||
}))
|
||||
}),
|
||||
]
|
||||
4
mocks/server.js
Normal file
4
mocks/server.js
Normal file
@@ -0,0 +1,4 @@
|
||||
import {setupServer} from "msw/node";
|
||||
import {handler} from "./handler";
|
||||
|
||||
export const server = setupServer(...handler)
|
||||
18
package.json
18
package.json
@@ -10,7 +10,11 @@
|
||||
"lint": "next lint",
|
||||
"update": "run-script-os",
|
||||
"update:win32": "echo 'ok'",
|
||||
"update:linux": "rm -rf .next/ && npm i && npm run build && sudo systemctl restart loan_facilities_develop_expert.service"
|
||||
"update:linux": "rm -rf .next/ && npm i && npm run build && sudo systemctl restart loan_facilities_develop_expert.service",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watchAll",
|
||||
"cypress:open": "cypress open",
|
||||
"cypress:run": "cypress run"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.10.6",
|
||||
@@ -50,7 +54,17 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@faker-js/faker": "^7.6.0",
|
||||
"eslint-config-next": "^13.3.0",
|
||||
"@testing-library/jest-dom": "^6.1.4",
|
||||
"@testing-library/react": "^14.0.0",
|
||||
"@testing-library/user-event": "^14.5.1",
|
||||
"cypress": "^13.4.0",
|
||||
"eslint-config-next": "^13.5.6",
|
||||
"eslint-plugin-jest-dom": "^5.1.0",
|
||||
"eslint-plugin-testing-library": "^6.1.0",
|
||||
"jest": "^29.7.0",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"msw": "^1.3.1",
|
||||
"next-router-mock": "^0.9.10",
|
||||
"run-script-os": "^1.1.6"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"app_name": "سامانه جامع تسهیلات",
|
||||
"app_short_name": "سامانه تسهیلات",
|
||||
"dashboard": "داشبورد",
|
||||
"powered_by_witel": "توسعه یافته توسط وایتل",
|
||||
"first_page": "خوش آمدید",
|
||||
"login": "ورود",
|
||||
"login_expert": "ورود کارشناس",
|
||||
|
||||
61
src/components/first/__tests__/index.test.js
Normal file
61
src/components/first/__tests__/index.test.js
Normal file
@@ -0,0 +1,61 @@
|
||||
import {render, screen, waitFor} from "@testing-library/react";
|
||||
import FirstComponent from "@/components/first";
|
||||
import MockAppWithProviders from "../../../../mocks/AppWithProvider";
|
||||
import {server} from "../../../../mocks/server";
|
||||
import {rest} from "msw";
|
||||
import {GET_USER_ROUTE} from "@/core/data/apiRoutes";
|
||||
|
||||
describe("First Component From First Page", () => {
|
||||
describe("Rendering", () => {
|
||||
it("App Name Text Rendered", () => {
|
||||
render(<MockAppWithProviders><FirstComponent/></MockAppWithProviders>);
|
||||
const appNameElement = screen.queryByText(/سامانه جامع تسهیلات/i);
|
||||
expect(appNameElement).toBeInTheDocument()
|
||||
});
|
||||
it("App version Text Rendered", () => {
|
||||
render(<MockAppWithProviders><FirstComponent/></MockAppWithProviders>);
|
||||
const versionControler = screen.queryByText(process.env.NEXT_PUBLIC_API_VERSION, {exact: false});
|
||||
expect(versionControler).toBeInTheDocument()
|
||||
});
|
||||
it("Powered By Rendered With Currect URL", () => {
|
||||
render(<MockAppWithProviders><FirstComponent/></MockAppWithProviders>);
|
||||
const linkElement = screen.queryByText('توسعه یافته توسط وایتل');
|
||||
expect(linkElement).toBeInTheDocument()
|
||||
expect(linkElement).toHaveAttribute('href', process.env.NEXT_PUBLIC_POWERED_BY_URL);
|
||||
});
|
||||
});
|
||||
describe("Behavioral", () => {
|
||||
it("Show Login Button And Do Not Show Dashboard Button When User Is Not Authenticated", async () => {
|
||||
render(<MockAppWithProviders><FirstComponent/></MockAppWithProviders>);
|
||||
await waitFor(() => {
|
||||
const authenticationButtonLogin = screen.queryByText(/ورود کارشناس/i)
|
||||
const authenticationButtonDashboard = screen.queryByText(/داشبورد/i)
|
||||
expect(authenticationButtonLogin).toBeInTheDocument()
|
||||
expect(authenticationButtonDashboard).not.toBeInTheDocument()
|
||||
})
|
||||
});
|
||||
it("Show Dashboard Button And Do Not Show Login Button When User Is Authenticated", async () => {
|
||||
localStorage.setItem("_token", 'token');
|
||||
render(<MockAppWithProviders><FirstComponent/></MockAppWithProviders>);
|
||||
await waitFor(() => {
|
||||
const authenticationButtonLogin = screen.queryByText(/ورود کارشناس/i)
|
||||
const authenticationButtonDashboard = screen.queryByText(/داشبورد/i)
|
||||
expect(authenticationButtonLogin).not.toBeInTheDocument()
|
||||
expect(authenticationButtonDashboard).toBeInTheDocument()
|
||||
})
|
||||
});
|
||||
it("Show Login Button And Do Not Show Dashboard Button When User Authentication Is Expired", async () => {
|
||||
localStorage.setItem("_token", 'token');
|
||||
server.use(rest.get(GET_USER_ROUTE, (req, res, ctx) => {
|
||||
return res(ctx.status(401))
|
||||
}))
|
||||
render(<MockAppWithProviders><FirstComponent/></MockAppWithProviders>);
|
||||
await waitFor(() => {
|
||||
const authenticationButtonLogin = screen.queryByText(/ورود کارشناس/i)
|
||||
const authenticationButtonDashboard = screen.queryByText(/داشبورد/i)
|
||||
expect(authenticationButtonLogin).toBeInTheDocument()
|
||||
expect(authenticationButtonDashboard).not.toBeInTheDocument()
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,11 +1,10 @@
|
||||
import {NextLinkComposed} from "@/core/components/LinkRouting";
|
||||
import LinkRouting, {NextLinkComposed} from "@/core/components/LinkRouting";
|
||||
import CenterLayout from "@/layouts/CenterLayout";
|
||||
import FullPageLayout from "@/layouts/FullPageLayout";
|
||||
import useUser from "@/lib/app/hooks/useUser";
|
||||
import {Button, Stack, Typography} from "@mui/material";
|
||||
import {useTranslations} from "next-intl";
|
||||
import SvgDashboard from "@/core/components/svgs/SvgDashboard";
|
||||
import process from "next/dist/build/webpack/loaders/resolve-url-loader/lib/postcss";
|
||||
|
||||
const FirstComponent = () => {
|
||||
const t = useTranslations();
|
||||
@@ -18,28 +17,16 @@ const FirstComponent = () => {
|
||||
<Typography variant="h5" sx={{textAlign: "center"}}>
|
||||
{t("app_name")}
|
||||
</Typography>
|
||||
{isAuth ? (
|
||||
<Button
|
||||
variant="outlined"
|
||||
component={NextLinkComposed}
|
||||
to={{
|
||||
pathname: "/dashboard",
|
||||
}}
|
||||
>
|
||||
{t("dashboard")}
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
sx={{mx: 2}}
|
||||
variant="contained"
|
||||
component={NextLinkComposed}
|
||||
to={{
|
||||
pathname: "/login-expert",
|
||||
}}
|
||||
>
|
||||
{t("login_expert")}
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
data-testid="button-login-or-dashboard"
|
||||
variant={isAuth ? "outlined" : "contained"}
|
||||
component={NextLinkComposed}
|
||||
to={{
|
||||
pathname: isAuth ? "/dashboard" : "/login-expert",
|
||||
}}
|
||||
>
|
||||
{isAuth ? t("dashboard") : t("login_expert")}
|
||||
</Button>
|
||||
</CenterLayout>
|
||||
<Stack direction="row" alignItems="center" justifyContent="center">
|
||||
<Typography variant={"caption"}
|
||||
@@ -47,10 +34,22 @@ const FirstComponent = () => {
|
||||
color: 'primary.main',
|
||||
fontFamily: 'Arial',
|
||||
fontWeight: 'bold'
|
||||
}}>v{process.env.NEXT_PUBLIC_API_VERSION}</Typography>
|
||||
}}
|
||||
>
|
||||
v{process.env.NEXT_PUBLIC_API_VERSION}
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Stack direction="row" alignItems="center" justifyContent="center">
|
||||
<LinkRouting
|
||||
sx={{margin: 0.5, fontSize: "14px"}}
|
||||
href={process.env.NEXT_PUBLIC_POWERED_BY_URL}
|
||||
target="_blank"
|
||||
>
|
||||
{t("powered_by_witel")}
|
||||
</LinkRouting>
|
||||
</Stack>
|
||||
</FullPageLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default FirstComponent;
|
||||
export default FirstComponent;
|
||||
@@ -1,10 +1,10 @@
|
||||
import {useState} from "react";
|
||||
import Header from "./header";
|
||||
import Sidebar from "./sidebar";
|
||||
import {Toolbar} from "@mui/material";
|
||||
import BreadCrumbs from "./breadcrumbs";
|
||||
import FullPageLayout from "@/layouts/FullPageLayout";
|
||||
import RolePermissionMiddleware from "@/middlewares/RolePermission";
|
||||
import Header from "@/components/layouts/Dashboard/Header";
|
||||
import Sidebar from "@/components/layouts/Dashboard/Sidebar";
|
||||
import BreadCrumbs from "@/components/layouts/Dashboard/Breadcrumbs";
|
||||
|
||||
const drawerWidth = 240;
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ const App = ({Component, pageProps}) => {
|
||||
<>
|
||||
<UserProvider>
|
||||
<LanguageProvider>
|
||||
<NextIntlProvider messages={pageProps.messages || {}}>
|
||||
<NextIntlProvider locale={pageProps.locale} messages={pageProps.messages || {}}>
|
||||
<MuiLayout isBot={pageProps.isBot}>
|
||||
{pageProps.messages ? <TitlePage text={pageProps.title}/> : ''}
|
||||
<LoadingProvider>
|
||||
|
||||
@@ -16,6 +16,7 @@ export async function getServerSideProps({req, locale}) {
|
||||
messages: (await import(`&/locales/${locale}/app.json`)).default,
|
||||
title: "first_page",
|
||||
isBot,
|
||||
locale
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user