mirror of
https://github.com/iib0011/omni-tools.git
synced 2025-09-19 22:19:36 +02:00
test: init
This commit is contained in:
46
.github/workflows/ci.yml
vendored
Normal file
46
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main # or the branch you want to trigger the workflow on
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Node.js
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: '18' # Specify the Node.js version you want to use
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm install
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: npm run test
|
||||||
|
|
||||||
|
- name: Build project
|
||||||
|
run: npm run build
|
||||||
|
|
||||||
|
- name: Deploy to Netlify
|
||||||
|
uses: nwtgck/actions-netlify@v1.2
|
||||||
|
with:
|
||||||
|
publish-dir: ./build
|
||||||
|
production-branch: main
|
||||||
|
deploy-message: Deploy from GitHub Actions
|
||||||
|
enable-pull-request-comment: true
|
||||||
|
enable-commit-comment: true
|
||||||
|
overwrites-pull-request-comment: true
|
||||||
|
env:
|
||||||
|
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||||
|
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
|
||||||
|
timeout-minutes: 1
|
||||||
|
|
@@ -20,7 +20,8 @@
|
|||||||
"test:ui": "vitest --ui",
|
"test:ui": "vitest --ui",
|
||||||
"lint": "eslint src --max-warnings=0",
|
"lint": "eslint src --max-warnings=0",
|
||||||
"typecheck": "tsc --project tsconfig.json --noEmit",
|
"typecheck": "tsc --project tsconfig.json --noEmit",
|
||||||
"prepare": "husky install"
|
"prepare": "husky install",
|
||||||
|
"prebuild": "npm run test"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.11.4",
|
"@emotion/react": "^11.11.4",
|
||||||
|
@@ -1,32 +0,0 @@
|
|||||||
import { render, screen } from '@testing-library/react'
|
|
||||||
|
|
||||||
import App from './App'
|
|
||||||
|
|
||||||
describe('<App />', () => {
|
|
||||||
it('should render the App', () => {
|
|
||||||
const { container } = render(<App />)
|
|
||||||
|
|
||||||
expect(
|
|
||||||
screen.getByRole('heading', {
|
|
||||||
name: /Welcome!/i,
|
|
||||||
level: 1
|
|
||||||
})
|
|
||||||
).toBeInTheDocument()
|
|
||||||
|
|
||||||
expect(
|
|
||||||
screen.getByText(
|
|
||||||
/This is a boilerplate build with Vite, React 18, TypeScript, Vitest, Testing Library, TailwindCSS 3, Eslint and Prettier./i
|
|
||||||
)
|
|
||||||
).toBeInTheDocument()
|
|
||||||
|
|
||||||
expect(
|
|
||||||
screen.getByRole('link', {
|
|
||||||
name: /start building for free/i
|
|
||||||
})
|
|
||||||
).toBeInTheDocument()
|
|
||||||
|
|
||||||
expect(screen.getByRole('img')).toBeInTheDocument()
|
|
||||||
|
|
||||||
expect(container.firstChild).toBeInTheDocument()
|
|
||||||
})
|
|
||||||
})
|
|
@@ -1,4 +1,4 @@
|
|||||||
export {default as useDebounce} from "./useDebounce";
|
export { default as useDebounce } from './useDebounce';
|
||||||
export {default as useTimeout} from "./useTimeout";
|
export { default as useTimeout } from './useTimeout';
|
||||||
export {default as usePrevious} from "./usePrevious";
|
export { default as usePrevious } from './usePrevious';
|
||||||
export {default as useUpdateEffect} from "./useUpdateEffect";
|
export { default as useUpdateEffect } from './useUpdateEffect';
|
||||||
|
@@ -9,10 +9,9 @@ import ToolTextResult from '../../../components/result/ToolTextResult';
|
|||||||
import { Field, Formik, FormikProps, useFormikContext } from 'formik';
|
import { Field, Formik, FormikProps, useFormikContext } from 'formik';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import ToolOptions from '../../../components/ToolOptions';
|
import ToolOptions from '../../../components/ToolOptions';
|
||||||
import { splitIntoChunks, splitTextByLength } from './service';
|
import { compute, SplitOperatorType } from './service';
|
||||||
import { CustomSnackBarContext } from '../../../contexts/CustomSnackBarContext';
|
import { CustomSnackBarContext } from '../../../contexts/CustomSnackBarContext';
|
||||||
|
|
||||||
type SplitOperatorType = 'symbol' | 'regex' | 'length' | 'chunks';
|
|
||||||
const initialValues = {
|
const initialValues = {
|
||||||
splitSeparatorType: 'symbol' as SplitOperatorType,
|
splitSeparatorType: 'symbol' as SplitOperatorType,
|
||||||
symbolValue: ' ',
|
symbolValue: ' ',
|
||||||
@@ -160,24 +159,20 @@ export default function SplitText() {
|
|||||||
regexValue,
|
regexValue,
|
||||||
lengthValue
|
lengthValue
|
||||||
} = values;
|
} = values;
|
||||||
let splitText;
|
|
||||||
switch (splitSeparatorType) {
|
setResult(
|
||||||
case 'symbol':
|
compute(
|
||||||
splitText = input.split(symbolValue);
|
splitSeparatorType,
|
||||||
break;
|
input,
|
||||||
case 'regex':
|
symbolValue,
|
||||||
splitText = input.split(new RegExp(regexValue));
|
regexValue,
|
||||||
break;
|
Number(lengthValue),
|
||||||
case 'length':
|
Number(chunksValue),
|
||||||
splitText = splitTextByLength(input, Number(lengthValue));
|
charBeforeChunk,
|
||||||
break;
|
charAfterChunk,
|
||||||
case 'chunks':
|
outputSeparator
|
||||||
splitText = splitIntoChunks(input, Number(chunksValue)).map(
|
)
|
||||||
(chunk) => `${charBeforeChunk}${chunk}${charAfterChunk}`
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
const res = splitText.join(outputSeparator);
|
|
||||||
setResult(res);
|
|
||||||
} catch (exception: unknown) {
|
} catch (exception: unknown) {
|
||||||
if (exception instanceof Error)
|
if (exception instanceof Error)
|
||||||
showSnackBar(exception.message, 'error');
|
showSnackBar(exception.message, 'error');
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
export function splitTextByLength(text: string, length: number) {
|
export type SplitOperatorType = 'symbol' | 'regex' | 'length' | 'chunks';
|
||||||
|
|
||||||
|
function splitTextByLength(text: string, length: number) {
|
||||||
if (length <= 0) throw new Error('Length must be a positive number');
|
if (length <= 0) throw new Error('Length must be a positive number');
|
||||||
const result: string[] = [];
|
const result: string[] = [];
|
||||||
for (let i = 0; i < text.length; i += length) {
|
for (let i = 0; i < text.length; i += length) {
|
||||||
@@ -7,7 +9,7 @@ export function splitTextByLength(text: string, length: number) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function splitIntoChunks(text: string, numChunks: number) {
|
function splitIntoChunks(text: string, numChunks: number) {
|
||||||
if (numChunks <= 0)
|
if (numChunks <= 0)
|
||||||
throw new Error('Number of chunks must be a positive number');
|
throw new Error('Number of chunks must be a positive number');
|
||||||
const totalLength = text.length;
|
const totalLength = text.length;
|
||||||
@@ -31,3 +33,33 @@ export function splitIntoChunks(text: string, numChunks: number) {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function compute(
|
||||||
|
splitSeparatorType: SplitOperatorType,
|
||||||
|
input: string,
|
||||||
|
symbolValue: string,
|
||||||
|
regexValue: string,
|
||||||
|
lengthValue: number,
|
||||||
|
chunksValue: number,
|
||||||
|
charBeforeChunk: string,
|
||||||
|
charAfterChunk: string,
|
||||||
|
outputSeparator: string
|
||||||
|
) {
|
||||||
|
let splitText;
|
||||||
|
switch (splitSeparatorType) {
|
||||||
|
case 'symbol':
|
||||||
|
splitText = input.split(symbolValue);
|
||||||
|
break;
|
||||||
|
case 'regex':
|
||||||
|
splitText = input.split(new RegExp(regexValue));
|
||||||
|
break;
|
||||||
|
case 'length':
|
||||||
|
splitText = splitTextByLength(input, lengthValue);
|
||||||
|
break;
|
||||||
|
case 'chunks':
|
||||||
|
splitText = splitIntoChunks(input, chunksValue).map(
|
||||||
|
(chunk) => `${charBeforeChunk}${chunk}${charAfterChunk}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return splitText.join(outputSeparator);
|
||||||
|
}
|
||||||
|
72
src/pages/string/split/string-split.test.ts
Normal file
72
src/pages/string/split/string-split.test.ts
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { compute } from './service';
|
||||||
|
|
||||||
|
describe('compute function', () => {
|
||||||
|
it('should split by symbol', () => {
|
||||||
|
const result = compute('symbol', 'hello world', ' ', '', 0, 0, '', '', ',');
|
||||||
|
expect(result).toBe('hello,world');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should split by regex', () => {
|
||||||
|
const result = compute(
|
||||||
|
'regex',
|
||||||
|
'hello1world2again',
|
||||||
|
'',
|
||||||
|
'\\d',
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
','
|
||||||
|
);
|
||||||
|
expect(result).toBe('hello,world,again');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should split by length', () => {
|
||||||
|
const result = compute('length', 'helloworld', '', '', 3, 0, '', '', ',');
|
||||||
|
expect(result).toBe('hel,low,orl,d');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should split into chunks', () => {
|
||||||
|
const result = compute(
|
||||||
|
'chunks',
|
||||||
|
'helloworldagain',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
0,
|
||||||
|
3,
|
||||||
|
'[',
|
||||||
|
']',
|
||||||
|
','
|
||||||
|
);
|
||||||
|
expect(result).toBe('[hello],[world],[again]');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle empty input', () => {
|
||||||
|
const result = compute('symbol', '', ' ', '', 0, 0, '', '', ',');
|
||||||
|
expect(result).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle length greater than text length', () => {
|
||||||
|
const result = compute('length', 'hi', '', '', 5, 0, '', '', ',');
|
||||||
|
expect(result).toBe('hi');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle chunks greater than text length', () => {
|
||||||
|
expect(() => {
|
||||||
|
compute('chunks', 'hi', '', '', 0, 5, '', '', ',');
|
||||||
|
}).toThrow('Text length must be at least as long as the number of chunks');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle invalid length', () => {
|
||||||
|
expect(() => {
|
||||||
|
compute('length', 'hello', '', '', -1, 0, '', '', ',');
|
||||||
|
}).toThrow('Length must be a positive number');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle invalid chunks', () => {
|
||||||
|
expect(() => {
|
||||||
|
compute('chunks', 'hello', '', '', 0, 0, '', '', ',');
|
||||||
|
}).toThrow('Number of chunks must be a positive number');
|
||||||
|
});
|
||||||
|
});
|
@@ -1,7 +1,7 @@
|
|||||||
/// <reference types="vitest" />
|
/// <reference types="vitest" />
|
||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite';
|
||||||
import react from '@vitejs/plugin-react-swc'
|
import react from '@vitejs/plugin-react-swc';
|
||||||
import tsconfigPaths from 'vite-tsconfig-paths'
|
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||||
|
|
||||||
// https://vitejs.dev/config https://vitest.dev/config
|
// https://vitejs.dev/config https://vitest.dev/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
@@ -10,6 +10,6 @@ export default defineConfig({
|
|||||||
globals: true,
|
globals: true,
|
||||||
environment: 'happy-dom',
|
environment: 'happy-dom',
|
||||||
setupFiles: '.vitest/setup',
|
setupFiles: '.vitest/setup',
|
||||||
include: ['**/test.{ts,tsx}']
|
include: ['**/*.test.{ts,tsx}']
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
Reference in New Issue
Block a user