Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug/3 #5

Merged
merged 5 commits into from
Apr 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,039 changes: 1,024 additions & 15 deletions package-lock.json

Large diffs are not rendered by default.

11 changes: 8 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,12 @@
},
"license": "MIT",
"devDependencies": {
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^12.0.0",
"@testing-library/react-hooks": "^8.0.1",
"@testing-library/user-event": "^14.4.3",
"@types/jest": "^29.5.0",
"@types/react": "17.0.0",
"@types/react": "^17.0.2",
"@typescript-eslint/eslint-plugin": "^5.57.0",
"@typescript-eslint/parser": "^5.57.0",
"eslint": "^8.37.0",
Expand All @@ -47,6 +50,7 @@
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^4.2.1",
"jest": "^29.5.0",
"jest-environment-jsdom": "^29.5.0",
"react": "17.0.2",
"react-test-renderer": "^17.0.2",
"ts-jest": "^29.0.5",
Expand Down Expand Up @@ -81,7 +85,8 @@
],
"moduleNameMapper": {
"^#(.*)$": "<rootDir>/useTaskQueue/$1",
"^!(.*)$": "<rootDir>/test/$1"
"^!(.*)$": "<rootDir>/test/$1",
"^@21gram-consulting/use-task-queue": "<rootDir>/useTaskQueue/index.ts"
},
"coverageThreshold": {
"global": {
Expand All @@ -99,4 +104,4 @@
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* @jest-environment jsdom
*/
import {descriptors} from '#consistencyGuard';
import {render, screen} from '@testing-library/react';
import '@testing-library/jest-dom';
import ReproApp from './ReproApp';
import userEvent from '@testing-library/user-event';

beforeEach(() => {
descriptors.clear();
});
afterEach(() => {
jest.clearAllMocks();
});

describe('Hook Reevaluation due to task recreation:', () => {
test("shouldn't end up in an infinite loop when a failure occurs.", async () => {
render(<ReproApp />);
let repetition = 50;
while (repetition--) {
await userEvent.click(screen.getByText('Reproduce Bug'));
}
await screen.findByTestId('output');

expect(screen.getByTestId('output')).toHaveTextContent('For an input');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {useReproContext} from './ReproContext';
import {FunctionComponent} from 'react';
import {TaskQueueHook} from '@21gram-consulting/use-task-queue';
import Task1 from './Task1';
import Task2 from './Task2';
import Task3 from './Task3';

const OngoingTasks: FunctionComponent = function OngoingTasks() {
const context = useReproContext();
return (
<div>
<h1>Ongoing Tasks</h1>
<div>
<h2>{subTitle(context.task1)}</h2>
<Task1 />
</div>
<div>
<h2>{subTitle(context.task2)}</h2>
<Task2 />
</div>
<div>
<h2>{subTitle(context.task3)}</h2>
<Task3 />
</div>
</div>
);
};

const subTitle = <A, B>({
input,
process,
error,
output,
}: TaskQueueHook<A, B>): string =>
[
`${input.length} waiting to be picked up`,
`${process.length} is processed.`,
`${error.length} resolved as error`,
`${output.length} result ready for pickup`,
]
.join(', ')
.concat('.');

export default OngoingTasks;
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {FunctionComponent} from 'react';
import {ReproProvider} from './ReproContext';
import OngoingTasks from './OngoingTasks';
import Reproduce from './Reproduce';

const ReproApp: FunctionComponent = function ReproApp() {
return (
<ReproProvider>
<OngoingTasks />
<Reproduce />
</ReproProvider>
);
};

export default ReproApp;
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {
FunctionComponent,
PropsWithChildren,
createContext,
useContext,
} from 'react';
import {nullTaskQueue, TaskQueueHook} from '@21gram-consulting/use-task-queue';
import useTask1 from './useTask1';
import useTask2 from './useTask2';
import useTask3 from './useTask3';

type Props = PropsWithChildren<unknown>;
export type ContextValue = {
task1: TaskQueueHook<number, number>;
task2: TaskQueueHook<number, string>;
task3: TaskQueueHook<string, string[]>;
};

const ReproContext = createContext<ContextValue>({
task1: nullTaskQueue(),
task2: nullTaskQueue(),
task3: nullTaskQueue(),
});

export function useReproContext(): ContextValue {
return useContext(ReproContext);
}

export const ReproProvider: FunctionComponent<Props> = props => {
const task1 = useTask1();
const task2 = useTask2(task1);
const task3 = useTask3(task2);
return (
<ReproContext.Provider value={{task1, task2, task3}}>
{props.children}
</ReproContext.Provider>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {FunctionComponent, useState} from 'react';
import {useReproContext} from './ReproContext';

const Reproduce: FunctionComponent = () => {
const context = useReproContext();
const [input, setInput] = useState(10);
const reproduce = () => {
context.task1.push(input);
setInput(v => v + 1);
};
return (
<>
<button onClick={reproduce}>Reproduce Bug</button>
<output data-testid="output">
<ul>
{context.task3.output.map((output, index) => (
<li key={index}>
For an input of {output.input}, the outputs are:
<ul>
{output.output.map((output, index) => (
<li key={index}>{output}</li>
))}
</ul>
</li>
))}
</ul>
</output>
</>
);
};

export default Reproduce;
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {FunctionComponent} from 'react';
import {TaskProcess} from '@21gram-consulting/use-task-queue';
import {useReproContext} from './ReproContext';

const Task1: FunctionComponent = function Task1() {
const context = useReproContext();
return (
<div>
<h3>Task 1</h3>
<div>
{context.task1.process.map((process, index) => (
<TaskItem
{...process}
key={index}
cancel={() => context.task1.kill(process)}
/>
))}
</div>
</div>
);
};

type TaskItemProps = TaskProcess<number, number> & {
cancel: () => void;
};
const TaskItem: FunctionComponent<TaskItemProps> = props => {
return (
<div>
<h4>Task Item</h4>
<p>Processing input: {props.input}</p>
<button onClick={props.cancel}>Cancel</button>
</div>
);
};

export default Task1;
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {FunctionComponent} from 'react';
import {TaskProcess} from '@21gram-consulting/use-task-queue';
import {useReproContext} from './ReproContext';

const Task2: FunctionComponent = function Task2() {
const context = useReproContext();
return (
<div>
<h3>Task 1</h3>
<div>
{context.task2.process.map((process, index) => (
<TaskItem
{...process}
key={index}
cancel={() => context.task2.kill(process)}
/>
))}
</div>
</div>
);
};

type TaskItemProps = TaskProcess<number, string> & {
cancel: () => void;
};
const TaskItem: FunctionComponent<TaskItemProps> = props => {
return (
<div>
<h4>Task Item</h4>
<p>Processing input: {props.input}</p>
<button onClick={props.cancel}>Cancel</button>
</div>
);
};

export default Task2;
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {FunctionComponent} from 'react';
import {TaskProcess} from '@21gram-consulting/use-task-queue';
import {useReproContext} from './ReproContext';

const Task3: FunctionComponent = function Task3() {
const context = useReproContext();
return (
<div>
<h3>Task 1</h3>
<div>
{context.task3.process.map((process, index) => (
<TaskItem
{...process}
key={index}
cancel={() => context.task3.kill(process)}
/>
))}
</div>
</div>
);
};

type TaskItemProps = TaskProcess<string, string[]> & {
cancel: () => void;
};
const TaskItem: FunctionComponent<TaskItemProps> = props => {
return (
<div>
<h4>Task Item</h4>
<p>Processing input: {props.input}</p>
<button onClick={props.cancel}>Cancel</button>
</div>
);
};

export default Task3;
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import {TaskQueueHook, useTaskQueue} from '@21gram-consulting/use-task-queue';
import {json} from '@21gram-consulting/ts-codec';

export default function useTask1(): TaskQueueHook<number, number> {
return useTaskQueue({
name: 'task1',
codec: json.number,
task: v => {
if (v % 3 === 0) throw new Error('Stub task execution error.');
return [v, v * 2, v * 3];
},
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import {TaskQueueHook, useTaskQueue} from '@21gram-consulting/use-task-queue';
import {json} from '@21gram-consulting/ts-codec';

export default function useTask2(
input: TaskQueueHook<any, number>
): TaskQueueHook<number, string> {
return useTaskQueue({
name: 'task2',
codec: json.number,
input: input,
task: v => [v.toString()],
precondition: v => v % 2 === 0,
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import {TaskQueueHook, useTaskQueue} from '@21gram-consulting/use-task-queue';
import {json} from '@21gram-consulting/ts-codec';

export default function useTask3(
input: TaskQueueHook<any, string>
): TaskQueueHook<string, string[]> {
return useTaskQueue({
name: 'task3',
codec: json.string,
input: input,
task: v => [v.split('')],
postcondition: v => v.length === 2,
});
}
Loading