Merged PR 91: [1.3.0] Project upgrade

- Refactor tests to use @testing-library/react and update dependencies
- .
- Refactor ESLint configuration to use modern imports and remove deprecated notes file
- Update ESLint and TypeScript configurations for improved compatibility and modern syntax
- Update TypeScript target and ESLint configuration for improved compatibility
- Update Jest configuration, refactor tests, and add date utility function
- Update ESLint configuration to include React settings and enhance rules
master
Tudor Stanciu 2025-04-06 15:30:40 +00:00
parent 3779dae50b
commit 47c73d8eaf
17 changed files with 3679 additions and 7693 deletions

View File

@ -1,2 +0,0 @@
node_modules
dist

View File

@ -1,32 +0,0 @@
{
"root": true,
"extends": [
"prettier",
"plugin:prettier/recommended",
"eslint:recommended",
"plugin:react/recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
],
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint", "prettier", "react", "react-hooks"],
"rules": {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-explicit-any": "off"
},
"settings": {
"react": {
"version": "detect"
}
},
"env": {
"browser": true,
"node": true
},
"globals": {
"JSX": true
}
}

151
README.md
View File

@ -1,51 +1,132 @@
# Tuitio client react
# Tuitio Client React
## Introduction
Tuitio client react is an npm package written in typescript that facilitates the integration of a react application with [Tuitio](https://lab.code-rove.com/gitea/tudor.stanciu/tuitio).
**Tuitio Client React** is an npm package written in TypeScript that simplifies the integration of a React application with [Tuitio](https://lab.code-rove.com/gitea/tudor.stanciu/tuitio).
This package uses [tuitio-client](https://lab.code-rove.com/gitea/bricks/tuitio-client#readme) internally and adds state management through react contexts and various react hooks through which the user has access to data and actions.
This package builds upon [tuitio-client](https://lab.code-rove.com/gitea/bricks/tuitio-client#readme) by adding state management via React Context and providing various hooks for seamless access to data and actions.
## Package installation
## Installation
The package installation can be done in two ways:
- from the command line: `npm install @flare/tuitio-client-react`
- from the package.json file: `"@flare/tuitio-client-react": "1.0.0"`
## How to use the package
```javascript!
const { TuitioProvider, useTuitioClient, useTuitioUser, useTuitioToken, useTuitioUserInfo } = require("@flare/tuitio-client-react");
const TuitioProvider = require("@flare/tuitio-client-react");
Install the package using **npm**:
```sh
npm install @flare/tuitio-client-react --registry https://lab.code-rove.com/public-node-registry
```
```javascript!
import { TuitioProvider, useTuitioClient, useTuitioUser, useTuitioToken, useTuitioUserInfo } from "@flare/tuitio-client-react";
import TuitioProvider from "@flare/tuitio-client-react";
Alternatively, add it directly to your `package.json` file:
```json
"dependencies": {
"@flare/tuitio-client-react": "1.0.0"
}
```
## Usage
### Importing the package
Using **CommonJS**:
```javascript
const {
TuitioProvider,
useTuitioClient,
useTuitioUser,
useTuitioToken,
useTuitioUserInfo
} = require("@flare/tuitio-client-react");
```
Using **ES Modules**:
```javascript
import {
TuitioProvider,
useTuitioClient,
useTuitioUser,
useTuitioToken,
useTuitioUserInfo
} from "@flare/tuitio-client-react";
```
### Example usage
Wrap your application with `TuitioProvider`:
```jsx
import { TuitioProvider } from "@flare/tuitio-client-react";
function App() {
return (
<TuitioProvider>
<YourComponent />
</TuitioProvider>
);
}
```
Access Tuitio services via hooks:
```jsx
import { useTuitioUser, useTuitioToken } from "@flare/tuitio-client-react";
function UserProfile() {
const user = useTuitioUser();
const token = useTuitioToken();
return (
<div>
<h1>Welcome, {user.name}</h1>
<p>Your token: {token}</p>
</div>
);
}
```
## Unit testing
Unit testing is done using [Jest](https://jestjs.io/). This is an awesome testing framework created by Facebook.
The files containing tests are identified by the extension `*.test.ts`.
All tests in the package can be executed by running: `npm test`.
Unit tests are written using [Jest](https://jestjs.io/).
- Test files follow the naming convention `*.test.ts`.
- Run all tests with:
```sh
npm test
```
## Changelog
1.0.0 - Package initialization.
1.0.1 - Added useTuitioClient default options.
1.1.0 - In this version, the account logout method and the latest changes published by Tuitio were implemented.
1.1.1 - Account logout bug fix.
1.2.0 - Has been implemented the "user-info" method exposed by the Tuitio API and the latest changes published by "@flare/tuitio-client".
1.2.1 - Added decodedToken in useTuitioToken hook. The token is obtained directly from TuitioProvider's react context.
1.2.2 - Upgraded @flare/tuitio-client and @flare/js-utils packages.
1.2.3 - User roles were handled in useTuitioUserInfo hook.
1.2.4 - Upgraded @flare/tuitio-client.
1.2.5 - Upgraded @flare/tuitio-client.
1.2.6 - Removed specific user groups and roles. Records must be dynamic.
1.2.7 - Set react version range in peer dependencies.
1.2.8 - Include the src directory in the npm package.
1.2.9 - Upgraded @flare/tuitio-client.
1.2.10 - Upgraded @flare/tuitio-client.
### 1.3.0
- Migrated from `@flare/js-utils` to `@flare/utiliyo`.
- Upgraded dependencies to their latest versions.
- Refactored codebase to use ES6 modules and syntax.
### 1.2.x
- **1.2.10** - Upgraded `@flare/tuitio-client`.
- **1.2.9** - Upgraded `@flare/tuitio-client`.
- **1.2.8** - Included the `src` directory in the npm package.
- **1.2.7** - Set React version range in peer dependencies.
- **1.2.6** - Removed hardcoded user groups and roles; records are now dynamic.
- **1.2.5** - Upgraded `@flare/tuitio-client`.
- **1.2.4** - Upgraded `@flare/tuitio-client`.
- **1.2.3** - Handled user roles in `useTuitioUserInfo` hook.
- **1.2.2** - Upgraded `@flare/tuitio-client` and `@flare/js-utils`.
- **1.2.1** - Added `decodedToken` in `useTuitioToken` hook.
- **1.2.0** - Implemented `user-info` method from Tuitio API.
### 1.1.x
- **1.1.1** - Fixed account logout bug.
- **1.1.0** - Implemented account logout and latest Tuitio API changes.
### 1.0.x
- **1.0.1** - Added default options for `useTuitioClient`.
- **1.0.0** - Initial package release.
---
For further details, contributions, or issue reporting, visit the [repository](https://lab.code-rove.com/gitea/bricks/tuitio-client-react).

42
eslint.config.mjs Normal file
View File

@ -0,0 +1,42 @@
import eslint from "@eslint/js";
import tseslint from "typescript-eslint";
import globals from "globals";
import react from "eslint-plugin-react";
import reactHooks from "eslint-plugin-react-hooks";
import prettier from "eslint-plugin-prettier";
export default tseslint.config(
{
ignores: ["node_modules", "dist"]
},
{
settings: {
react: {
version: "detect"
}
}
},
eslint.configs.recommended,
tseslint.configs.recommended,
react.configs.flat.recommended,
{
files: ["**/*.{ts,tsx}"],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser
},
plugins: {
react,
"react-hooks": reactHooks,
prettier
},
rules: {
...reactHooks.configs.recommended.rules,
"@typescript-eslint/no-explicit-any": "off",
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
"@typescript-eslint/no-unused-expressions": "off",
"@typescript-eslint/no-require-imports": "off"
}
}
);

View File

@ -13,5 +13,6 @@
"lines": 50,
"statements": 50
}
}
},
"testPathIgnorePatterns": ["/node_modules/", "/tests/utils/"]
}

10945
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "@flare/tuitio-client-react",
"version": "1.2.10",
"version": "1.3.0",
"description": "Tuitio client react is an npm package written in typescript that facilitates the integration of a react application with Tuitio.",
"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",
@ -49,31 +49,29 @@
"README.md"
],
"dependencies": {
"@flare/js-utils": "^1.1.0",
"@flare/tuitio-client": "^1.2.7"
"@flare/tuitio-client": "^1.3.0",
"@flare/utiliyo": "^1.2.1"
},
"peerDependencies": {
"react": ">=16.14.0 <19.0.0"
"react": ">=17.0.0"
},
"devDependencies": {
"@testing-library/react": "^12.1.5",
"@testing-library/react-hooks": "^7.0.2",
"@types/jest": "^29.4.0",
"@types/react": "^18.0.27",
"@typescript-eslint/eslint-plugin": "^5.51.0",
"@typescript-eslint/parser": "^5.51.0",
"eslint": "^8.34.0",
"eslint-config-prettier": "^8.6.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"jest": "^29.4.2",
"jest-canvas-mock": "^2.4.0",
"jest-environment-jsdom": "^29.4.2",
"prettier": "^2.8.4",
"react": "^16.14.0",
"react-dom": "16.14.0",
"ts-jest": "^29.0.5",
"typescript": "^4.9.5"
"@eslint/js": "^9.24.0",
"@testing-library/react": "^16.2.0",
"@types/jest": "^29.5.14",
"@types/react": "^19.1.0",
"eslint": "^9.24.0",
"eslint-plugin-prettier": "^5.2.5",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^5.2.0",
"jest": "^29.7.0",
"jest-canvas-mock": "^2.5.2",
"jest-environment-jsdom": "^29.7.0",
"prettier": "^3.5.3",
"react": "^19.1.0",
"react-dom": "19.1.0",
"ts-jest": "^29.3.1",
"typescript": "^5.8.3",
"typescript-eslint": "^8.29.0"
}
}

View File

@ -2,7 +2,7 @@
import { useContext, useState, useEffect } from "react";
import { TuitioContext } from "../contexts";
import { camelizeKeys } from "@flare/js-utils";
import { camelizeKeys } from "@flare/utiliyo";
import type { TuitioTokenData } from "@flare/tuitio-client";
const useTuitioDecodedToken = () => {

View File

@ -1,42 +1,51 @@
// Copyright (c) 2023 Tudor Stanciu
import { renderHook, act } from "@testing-library/react-hooks";
import { renderHook, act } from "@testing-library/react";
import { TuitioClient } from "@flare/tuitio-client";
import { useTuitioClient } from "../../src/hooks";
import { getTodayAtNoon } from "../utils/dateUtils";
const userName = "tuitio.user";
const password = "password";
const token = "mocked-token";
const expiresIn = 600000;
const validUntil = new Date();
jest.mock("@flare/tuitio-client", () => ({
TuitioClient: jest.fn().mockImplementation(() => ({
login: jest.fn().mockResolvedValue({
result: {
token,
expiresIn,
validUntil
},
error: null
}),
logout: jest.fn().mockResolvedValue({
result: {
userId: 0,
userName,
logoutDate: new Date()
},
error: null
jest.mock("@flare/tuitio-client", () => {
const userName = "tuitio.user";
const token = "mocked-token";
const expiresIn = 600000;
// Dynamically import getTodayAtNoon to avoid hoisting issues
const { getTodayAtNoon } = require("../utils/dateUtils");
const validUntil = getTodayAtNoon();
return {
TuitioClient: jest.fn().mockImplementation(() => ({
login: jest.fn().mockResolvedValue({
result: {
token,
expiresIn,
validUntil
},
error: null
}),
logout: jest.fn().mockResolvedValue({
result: {
userId: 0,
userName,
logoutDate: new Date()
},
error: null
})
})),
acquire: jest.fn().mockResolvedValue({
token,
validUntil,
userName
})
})),
acquire: jest.fn().mockResolvedValue({
token,
validUntil,
userName
})
}));
};
});
describe("useTuitioClient", () => {
const userName = "tuitio.user";
const password = "password";
const token = "mocked-token";
const expiresIn = 600000;
const validUntil = getTodayAtNoon();
it("should call onLoginSuccess when login is successful and should call onLogoutSuccess when logout is called", async () => {
const onLoginSuccess = jest.fn();
const onLogoutSuccess = jest.fn();

View File

@ -1,31 +1,32 @@
// Copyright (c) 2023 Tudor Stanciu
import React from "react";
import { renderHook } from "@testing-library/react-hooks";
import { renderHook } from "@testing-library/react";
import { TuitioContext, TuitioDispatchContext } from "../../src/contexts";
import { useTuitioClient } from "../../src/hooks";
const userName = "tuitio.user";
const token = "mocked-token";
const expiresIn = 600000;
jest.mock("@flare/tuitio-client", () => ({
TuitioClient: jest.fn().mockImplementation(() => ({
login: jest.fn().mockResolvedValue({
result: { token, expiresIn, validUntil: new Date() },
error: null
}),
logout: jest.fn().mockResolvedValue({
result: { userId: 0, userName, logoutDate: new Date() },
error: null
jest.mock("@flare/tuitio-client", () => {
const userName = "tuitio.user";
const token = "mocked-token";
const expiresIn = 600000;
return {
TuitioClient: jest.fn().mockImplementation(() => ({
login: jest.fn().mockResolvedValue({
result: { token, expiresIn, validUntil: new Date() },
error: null
}),
logout: jest.fn().mockResolvedValue({
result: { userId: 0, userName, logoutDate: new Date() },
error: null
})
})),
acquire: jest.fn().mockResolvedValue({
token,
validUntil: new Date(),
userName
})
})),
acquire: jest.fn().mockResolvedValue({
token,
validUntil: new Date(),
userName
})
}));
};
});
describe("useTuitioClient: dispatchActions", () => {
let spyOnLoginSuccess: (token: string, validUntil: Date, userName: string) => void;

View File

@ -1,7 +1,7 @@
// Copyright (c) 2023 Tudor Stanciu
import React from "react";
import { renderHook } from "@testing-library/react-hooks";
import { renderHook } from "@testing-library/react";
import { TuitioContext, TuitioDispatchContext } from "../../src/contexts";
import { useTuitioClient } from "../../src/hooks";
import axios from "axios";

View File

@ -1,7 +1,7 @@
// Copyright (c) 2023 Tudor Stanciu
import React from "react";
import { renderHook } from "@testing-library/react-hooks";
import { renderHook } from "@testing-library/react";
import { TuitioContext } from "../../src/contexts";
import { useTuitioDecodedToken } from "../../src/hooks";

View File

@ -1,7 +1,7 @@
// Copyright (c) 2023 Tudor Stanciu
import React from "react";
import { renderHook } from "@testing-library/react-hooks";
import { renderHook } from "@testing-library/react";
import { TuitioContext } from "../../src/contexts";
import { useTuitioToken } from "../../src/hooks";

View File

@ -1,7 +1,7 @@
// Copyright (c) 2023 Tudor Stanciu
import React from "react";
import { renderHook } from "@testing-library/react-hooks";
import { renderHook } from "@testing-library/react";
import { TuitioContext } from "../../src/contexts";
import { useTuitioUser } from "../../src/hooks";

View File

@ -1,7 +1,7 @@
// Copyright (c) 2023 Tudor Stanciu
import React, { useReducer, useMemo } from "react";
import { renderHook } from "@testing-library/react-hooks";
import { renderHook, waitFor } from "@testing-library/react";
import { TuitioContext, TuitioDispatchContext } from "../../src/contexts";
import { useTuitioUserInfo } from "../../src/hooks";
import { reducer, dispatchActions } from "../../src/reducer";
@ -66,20 +66,21 @@ describe("useTuitioUserInfo: dispatchActions", () => {
data: { ...userInfoMock }
});
const { result, waitForNextUpdate } = renderHook(useTuitioUserInfo, { wrapper: Wrapper });
const { result } = renderHook(useTuitioUserInfo, { wrapper: Wrapper });
expect(result.current.userInfo).toBeUndefined();
const onUserInfoLoadedSpy = jest.spyOn(dispatchedActions, "onUserInfoLoaded");
await waitForNextUpdate();
expect(onUserInfoLoadedSpy).toHaveBeenCalled();
expect(onUserInfoLoadedSpy).toHaveBeenCalledWith(userInfoMock);
await waitFor(() => {
expect(onUserInfoLoadedSpy).toHaveBeenCalled();
expect(onUserInfoLoadedSpy).toHaveBeenCalledWith(userInfoMock);
const { userInfo } = result.current;
expect(userInfo).toBeDefined();
expect(userInfo?.userId).toBe(1);
expect(userInfo?.userName).toBe("tuitio.test");
expect(userInfo?.userRoles).toHaveLength(2);
expect(userInfo?.userGroups).toHaveLength(1);
const { userInfo } = result.current;
expect(userInfo).toBeDefined();
expect(userInfo?.userId).toBe(1);
expect(userInfo?.userName).toBe("tuitio.test");
expect(userInfo?.userRoles).toHaveLength(2);
expect(userInfo?.userGroups).toHaveLength(1);
});
});
});

4
tests/utils/dateUtils.ts Normal file
View File

@ -0,0 +1,4 @@
export const getTodayAtNoon = () => {
const now = new Date();
return new Date(now.getFullYear(), now.getMonth(), now.getDate(), 12, 0, 0, 0);
};

View File

@ -2,7 +2,7 @@
"include": ["src"],
"exclude": ["dist", "node_modules"],
"compilerOptions": {
"target": "es5",
"target": "es6",
"module": "esnext",
"lib": ["dom", "esnext"],
"importHelpers": true,