@pie-lib/render-ui 4.35.3-next.0 → 4.35.3-next.155
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -81
- package/esm/package.json +3 -0
- package/lib/append-css-rules.js +10 -37
- package/lib/append-css-rules.js.map +1 -1
- package/lib/assets/enableAudioAutoplayImage.js +1 -2
- package/lib/assets/enableAudioAutoplayImage.js.map +1 -1
- package/lib/collapsible/index.js +35 -65
- package/lib/collapsible/index.js.map +1 -1
- package/lib/color.js +57 -201
- package/lib/color.js.map +1 -1
- package/lib/feedback.js +71 -108
- package/lib/feedback.js.map +1 -1
- package/lib/has-media.js +2 -7
- package/lib/has-media.js.map +1 -1
- package/lib/has-text.js +1 -7
- package/lib/has-text.js.map +1 -1
- package/lib/html-and-math.js +10 -30
- package/lib/html-and-math.js.map +1 -1
- package/lib/index.js +1 -24
- package/lib/index.js.map +1 -1
- package/lib/input-container.js +43 -44
- package/lib/input-container.js.map +1 -1
- package/lib/preview-layout.js +22 -58
- package/lib/preview-layout.js.map +1 -1
- package/lib/preview-prompt.js +104 -131
- package/lib/preview-prompt.js.map +1 -1
- package/lib/purpose.js +1 -7
- package/lib/purpose.js.map +1 -1
- package/lib/readable.js +1 -7
- package/lib/readable.js.map +1 -1
- package/lib/response-indicators.js +37 -86
- package/lib/response-indicators.js.map +1 -1
- package/lib/ui-layout.js +53 -70
- package/lib/ui-layout.js.map +1 -1
- package/lib/withUndoReset.js +51 -97
- package/lib/withUndoReset.js.map +1 -1
- package/package.json +23 -13
- package/src/__tests__/html-and-math.test.js +26 -14
- package/src/__tests__/preview-prompt.test.jsx +43 -40
- package/src/__tests__/purpose.test.jsx +27 -23
- package/src/__tests__/readable.test.jsx +34 -29
- package/src/__tests__/response-indicators.test.jsx +104 -9
- package/src/__tests__/ui-layout.test.jsx +28 -12
- package/src/__tests__/withUndoReset.test.jsx +110 -188
- package/src/collapsible/__tests__/index.test.jsx +33 -7
- package/src/collapsible/index.jsx +17 -17
- package/src/color.js +1 -5
- package/src/feedback.jsx +59 -63
- package/src/input-container.jsx +41 -32
- package/src/preview-layout.jsx +11 -23
- package/src/preview-prompt.jsx +76 -58
- package/src/response-indicators.jsx +22 -29
- package/src/ui-layout.jsx +41 -28
- package/src/withUndoReset.jsx +48 -50
- package/src/__tests__/__snapshots__/html-and-math.test.js.snap +0 -11
- package/src/__tests__/__snapshots__/preview-prompt.test.jsx.snap +0 -37
- package/src/__tests__/__snapshots__/purpose.test.jsx.snap +0 -42
- package/src/__tests__/__snapshots__/readable.test.jsx.snap +0 -64
- package/src/__tests__/__snapshots__/response-indicators.test.jsx.snap +0 -27
- package/src/collapsible/__tests__/__snapshots__/index.test.jsx.snap +0 -18
|
@@ -1,24 +1,23 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
3
|
import Readable from '../readable';
|
|
4
4
|
|
|
5
5
|
describe('Readable', () => {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
it('renders child unaltered', () => {
|
|
10
|
-
wrapper = mount(
|
|
6
|
+
describe('rendering', () => {
|
|
7
|
+
it('renders child with data-pie-readable attribute set to true', () => {
|
|
8
|
+
const { container } = render(
|
|
11
9
|
<Readable>
|
|
12
10
|
<div>text</div>
|
|
13
11
|
</Readable>,
|
|
14
12
|
);
|
|
15
|
-
|
|
16
|
-
expect(
|
|
17
|
-
|
|
18
|
-
expect(
|
|
13
|
+
|
|
14
|
+
expect(screen.getByText('text')).toBeInTheDocument();
|
|
15
|
+
const div = container.querySelector('div');
|
|
16
|
+
expect(div).toHaveAttribute('data-pie-readable', 'true');
|
|
19
17
|
});
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
|
|
19
|
+
it('renders multiple children with data-pie-readable attribute', () => {
|
|
20
|
+
const { container } = render(
|
|
22
21
|
<Readable>
|
|
23
22
|
<div>
|
|
24
23
|
<div>text1</div>
|
|
@@ -26,14 +25,17 @@ describe('Readable', () => {
|
|
|
26
25
|
</div>
|
|
27
26
|
</Readable>,
|
|
28
27
|
);
|
|
29
|
-
|
|
30
|
-
expect(
|
|
31
|
-
expect(
|
|
32
|
-
expect(
|
|
33
|
-
|
|
28
|
+
|
|
29
|
+
expect(screen.getByText('text1')).toBeInTheDocument();
|
|
30
|
+
expect(screen.getByText('text2')).toBeInTheDocument();
|
|
31
|
+
expect(screen.queryByText('text3')).not.toBeInTheDocument();
|
|
32
|
+
|
|
33
|
+
const parentDiv = container.querySelector('div');
|
|
34
|
+
expect(parentDiv).toHaveAttribute('data-pie-readable', 'true');
|
|
34
35
|
});
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
|
|
37
|
+
it('renders with data-pie-readable set to false when false prop is provided', () => {
|
|
38
|
+
const { container } = render(
|
|
37
39
|
<Readable false>
|
|
38
40
|
<div>
|
|
39
41
|
<div>text1</div>
|
|
@@ -41,14 +43,17 @@ describe('Readable', () => {
|
|
|
41
43
|
</div>
|
|
42
44
|
</Readable>,
|
|
43
45
|
);
|
|
44
|
-
|
|
45
|
-
expect(
|
|
46
|
-
expect(
|
|
47
|
-
expect(
|
|
48
|
-
|
|
46
|
+
|
|
47
|
+
expect(screen.getByText('text1')).toBeInTheDocument();
|
|
48
|
+
expect(screen.getByText('text2')).toBeInTheDocument();
|
|
49
|
+
expect(screen.queryByText('text3')).not.toBeInTheDocument();
|
|
50
|
+
|
|
51
|
+
const parentDiv = container.querySelector('div');
|
|
52
|
+
expect(parentDiv).toHaveAttribute('data-pie-readable', 'false');
|
|
49
53
|
});
|
|
50
|
-
|
|
51
|
-
|
|
54
|
+
|
|
55
|
+
it('renders with data-pie-readable set to false when false={true}', () => {
|
|
56
|
+
const { container } = render(
|
|
52
57
|
<Readable false={true}>
|
|
53
58
|
<div>
|
|
54
59
|
<div>text1</div>
|
|
@@ -56,9 +61,9 @@ describe('Readable', () => {
|
|
|
56
61
|
</div>
|
|
57
62
|
</Readable>,
|
|
58
63
|
);
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
expect(
|
|
64
|
+
|
|
65
|
+
const parentDiv = container.querySelector('div');
|
|
66
|
+
expect(parentDiv).toHaveAttribute('data-pie-readable', 'false');
|
|
62
67
|
});
|
|
63
68
|
});
|
|
64
69
|
});
|
|
@@ -1,16 +1,111 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import { Correct } from '../response-indicators';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import { Correct, Incorrect, PartiallyCorrect, NothingSubmitted } from '../response-indicators';
|
|
5
|
+
|
|
6
|
+
// Mock the icons
|
|
7
|
+
jest.mock('@pie-lib/icons', () => ({
|
|
8
|
+
Correct: () => <span data-testid="correct-icon">Correct Icon</span>,
|
|
9
|
+
Incorrect: () => <span data-testid="incorrect-icon">Incorrect Icon</span>,
|
|
10
|
+
PartiallyCorrect: () => <span data-testid="partially-correct-icon">Partially Correct Icon</span>,
|
|
11
|
+
NothingSubmitted: () => <span data-testid="nothing-submitted-icon">Nothing Submitted Icon</span>,
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
// Mock the Feedback component
|
|
15
|
+
jest.mock('../feedback', () => {
|
|
16
|
+
return function Feedback({ feedback, correctness }) {
|
|
17
|
+
return (
|
|
18
|
+
<div data-testid="feedback-content">
|
|
19
|
+
<div>{feedback}</div>
|
|
20
|
+
<div>{correctness}</div>
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
});
|
|
5
25
|
|
|
6
26
|
describe('response-indicators', () => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
27
|
+
describe('Correct indicator', () => {
|
|
28
|
+
it('renders correct icon without feedback', () => {
|
|
29
|
+
render(<Correct />);
|
|
30
|
+
expect(screen.getByTestId('correct-icon')).toBeInTheDocument();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('renders correct icon with feedback', () => {
|
|
34
|
+
render(<Correct feedback="Great job!" />);
|
|
35
|
+
expect(screen.getByTestId('correct-icon')).toBeInTheDocument();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('shows feedback popover when icon is clicked', async () => {
|
|
39
|
+
const user = userEvent.setup();
|
|
40
|
+
render(<Correct feedback="Great job!" />);
|
|
41
|
+
|
|
42
|
+
const icon = screen.getByTestId('correct-icon');
|
|
43
|
+
await user.click(icon);
|
|
44
|
+
|
|
45
|
+
expect(screen.getByTestId('feedback-content')).toBeInTheDocument();
|
|
46
|
+
expect(screen.getByText('Great job!')).toBeInTheDocument();
|
|
47
|
+
expect(screen.getByText('correct')).toBeInTheDocument();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('does not show popover when there is no feedback', async () => {
|
|
51
|
+
const user = userEvent.setup();
|
|
52
|
+
render(<Correct />);
|
|
53
|
+
|
|
54
|
+
const icon = screen.getByTestId('correct-icon');
|
|
55
|
+
await user.click(icon);
|
|
56
|
+
|
|
57
|
+
expect(screen.queryByTestId('feedback-content')).not.toBeInTheDocument();
|
|
58
|
+
});
|
|
10
59
|
});
|
|
11
60
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
61
|
+
describe('Incorrect indicator', () => {
|
|
62
|
+
it('renders incorrect icon', () => {
|
|
63
|
+
render(<Incorrect />);
|
|
64
|
+
expect(screen.getByTestId('incorrect-icon')).toBeInTheDocument();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('shows feedback with incorrect correctness', async () => {
|
|
68
|
+
const user = userEvent.setup();
|
|
69
|
+
render(<Incorrect feedback="Try again" />);
|
|
70
|
+
|
|
71
|
+
await user.click(screen.getByTestId('incorrect-icon'));
|
|
72
|
+
|
|
73
|
+
expect(screen.getByText('Try again')).toBeInTheDocument();
|
|
74
|
+
expect(screen.getByText('incorrect')).toBeInTheDocument();
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe('PartiallyCorrect indicator', () => {
|
|
79
|
+
it('renders partially correct icon', () => {
|
|
80
|
+
render(<PartiallyCorrect />);
|
|
81
|
+
expect(screen.getByTestId('partially-correct-icon')).toBeInTheDocument();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('shows feedback with partially-correct correctness', async () => {
|
|
85
|
+
const user = userEvent.setup();
|
|
86
|
+
render(<PartiallyCorrect feedback="Almost there" />);
|
|
87
|
+
|
|
88
|
+
await user.click(screen.getByTestId('partially-correct-icon'));
|
|
89
|
+
|
|
90
|
+
expect(screen.getByText('Almost there')).toBeInTheDocument();
|
|
91
|
+
expect(screen.getByText('partially-correct')).toBeInTheDocument();
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
describe('NothingSubmitted indicator', () => {
|
|
96
|
+
it('renders nothing submitted icon', () => {
|
|
97
|
+
render(<NothingSubmitted />);
|
|
98
|
+
expect(screen.getByTestId('nothing-submitted-icon')).toBeInTheDocument();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('shows feedback with nothing-submitted correctness', async () => {
|
|
102
|
+
const user = userEvent.setup();
|
|
103
|
+
render(<NothingSubmitted feedback="Please submit an answer" />);
|
|
104
|
+
|
|
105
|
+
await user.click(screen.getByTestId('nothing-submitted-icon'));
|
|
106
|
+
|
|
107
|
+
expect(screen.getByText('Please submit an answer')).toBeInTheDocument();
|
|
108
|
+
expect(screen.getByText('nothing-submitted')).toBeInTheDocument();
|
|
109
|
+
});
|
|
15
110
|
});
|
|
16
111
|
});
|
|
@@ -1,34 +1,50 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
3
|
import { UiLayout } from '../index';
|
|
4
4
|
|
|
5
5
|
describe('UiLayout', () => {
|
|
6
|
-
let wrapper;
|
|
7
6
|
const mockClasses = { extraCSSRules: 'extra-class' };
|
|
8
7
|
const fontSizeFactor = 1.5;
|
|
9
8
|
|
|
10
9
|
// Mock `getComputedStyle` to return a specific root font size.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
beforeAll(() => {
|
|
11
|
+
jest.spyOn(window, 'getComputedStyle').mockImplementation(() => ({
|
|
12
|
+
fontSize: '16px', // Default font size for root
|
|
13
|
+
}));
|
|
14
|
+
});
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
afterAll(() => {
|
|
17
|
+
jest.restoreAllMocks();
|
|
18
|
+
});
|
|
16
19
|
|
|
17
20
|
it('renders correctly', () => {
|
|
18
|
-
|
|
21
|
+
const { container } = render(<UiLayout classes={mockClasses} fontSizeFactor={fontSizeFactor} />);
|
|
22
|
+
expect(container.firstChild).toBeInTheDocument();
|
|
19
23
|
});
|
|
20
24
|
|
|
21
25
|
it('applies the correct classes', () => {
|
|
22
|
-
const
|
|
23
|
-
|
|
26
|
+
const { container } = render(<UiLayout className="custom-class" classes={mockClasses} fontSizeFactor={fontSizeFactor} />);
|
|
27
|
+
const div = container.querySelector('.custom-class');
|
|
28
|
+
expect(div).toBeInTheDocument();
|
|
24
29
|
});
|
|
25
30
|
|
|
26
31
|
it('computes style correctly based on fontSizeFactor', () => {
|
|
27
|
-
const
|
|
32
|
+
const { container } = render(<UiLayout classes={mockClasses} fontSizeFactor={fontSizeFactor} />);
|
|
33
|
+
const div = container.firstChild;
|
|
28
34
|
|
|
29
35
|
// Get the style property of the rendered div
|
|
30
|
-
const computedStyle = div.
|
|
31
|
-
// Assert the computed font size
|
|
36
|
+
const computedStyle = div.style;
|
|
37
|
+
// Assert the computed font size (16px * 1.5 = 24px)
|
|
32
38
|
expect(computedStyle.fontSize).toBe('24px');
|
|
33
39
|
});
|
|
40
|
+
|
|
41
|
+
it('renders children when provided', () => {
|
|
42
|
+
const { container } = render(
|
|
43
|
+
<UiLayout classes={mockClasses} fontSizeFactor={fontSizeFactor}>
|
|
44
|
+
<div className="test-child">Test Content</div>
|
|
45
|
+
</UiLayout>,
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
expect(container.querySelector('.test-child')).toBeInTheDocument();
|
|
49
|
+
});
|
|
34
50
|
});
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
2
4
|
import withUndoReset from '../withUndoReset';
|
|
3
|
-
import { mount, shallow } from 'enzyme';
|
|
4
|
-
import { shallowChild } from '@pie-lib/test-utils';
|
|
5
5
|
|
|
6
6
|
describe('withUndoReset', () => {
|
|
7
|
-
let wrapper;
|
|
8
7
|
let defaultProps;
|
|
8
|
+
let onSessionChange;
|
|
9
|
+
|
|
9
10
|
const WrappedClass = class WrappedComponent extends React.Component {
|
|
10
11
|
onSessionChange = (session) => {
|
|
11
12
|
this.props.onSessionChange(session);
|
|
@@ -20,7 +21,6 @@ describe('withUndoReset', () => {
|
|
|
20
21
|
|
|
21
22
|
onRemoveLastItem = () => {
|
|
22
23
|
const newItems = [...this.props.session.items];
|
|
23
|
-
|
|
24
24
|
newItems.pop();
|
|
25
25
|
|
|
26
26
|
this.onSessionChange({
|
|
@@ -31,13 +31,23 @@ describe('withUndoReset', () => {
|
|
|
31
31
|
|
|
32
32
|
render() {
|
|
33
33
|
const { session } = this.props;
|
|
34
|
-
const items = session.items ||
|
|
34
|
+
const items = session.items || [];
|
|
35
35
|
|
|
36
36
|
return (
|
|
37
|
-
<div>
|
|
38
|
-
{
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
<div data-testid="wrapped-component">
|
|
38
|
+
<button data-testid="add-item" onClick={() => this.onAddItem({ id: Date.now() })}>
|
|
39
|
+
Add Item
|
|
40
|
+
</button>
|
|
41
|
+
<button data-testid="remove-item" onClick={() => this.onRemoveLastItem()}>
|
|
42
|
+
Remove Item
|
|
43
|
+
</button>
|
|
44
|
+
<div data-testid="items-container">
|
|
45
|
+
{items.map((item) => (
|
|
46
|
+
<span key={item.id} data-testid={`item-${item.id}`}>
|
|
47
|
+
{item.id}
|
|
48
|
+
</span>
|
|
49
|
+
))}
|
|
50
|
+
</div>
|
|
41
51
|
</div>
|
|
42
52
|
);
|
|
43
53
|
}
|
|
@@ -45,210 +55,122 @@ describe('withUndoReset', () => {
|
|
|
45
55
|
|
|
46
56
|
const Component = withUndoReset(WrappedClass);
|
|
47
57
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
58
|
+
beforeEach(() => {
|
|
59
|
+
onSessionChange = jest.fn();
|
|
60
|
+
defaultProps = {
|
|
61
|
+
session: {
|
|
62
|
+
items: [],
|
|
63
|
+
},
|
|
64
|
+
onSessionChange,
|
|
65
|
+
};
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe('HOC functionality', () => {
|
|
69
|
+
it('renders the wrapped component', () => {
|
|
70
|
+
render(<Component {...defaultProps} />);
|
|
71
|
+
expect(screen.getByTestId('wrapped-component')).toBeInTheDocument();
|
|
56
72
|
});
|
|
57
73
|
|
|
58
|
-
it('
|
|
59
|
-
|
|
60
|
-
expect(
|
|
61
|
-
expect(
|
|
62
|
-
expect.objectContaining({
|
|
63
|
-
session: { items: [] },
|
|
64
|
-
}),
|
|
65
|
-
);
|
|
74
|
+
it('renders undo and reset buttons', () => {
|
|
75
|
+
render(<Component {...defaultProps} />);
|
|
76
|
+
expect(screen.getByText(/Undo/i)).toBeInTheDocument();
|
|
77
|
+
expect(screen.getByText(/Start Over/i)).toBeInTheDocument();
|
|
66
78
|
});
|
|
67
79
|
|
|
68
|
-
it('
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
expect(
|
|
80
|
+
it('passes session props to wrapped component', () => {
|
|
81
|
+
render(<Component {...defaultProps} />);
|
|
82
|
+
const itemsContainer = screen.getByTestId('items-container');
|
|
83
|
+
expect(itemsContainer).toBeInTheDocument();
|
|
72
84
|
});
|
|
85
|
+
});
|
|
73
86
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
wrapper
|
|
80
|
-
.find(WrappedClass)
|
|
81
|
-
.instance()
|
|
82
|
-
.onAddItem({ id: 1 });
|
|
83
|
-
|
|
84
|
-
expect(
|
|
85
|
-
wrapper
|
|
86
|
-
.find(WrappedClass)
|
|
87
|
-
.html()
|
|
88
|
-
.includes('span'),
|
|
89
|
-
).toEqual(true);
|
|
90
|
-
|
|
91
|
-
wrapper
|
|
92
|
-
.find(WrappedClass)
|
|
93
|
-
.instance()
|
|
94
|
-
.onRemoveLastItem();
|
|
95
|
-
|
|
96
|
-
expect(
|
|
97
|
-
wrapper
|
|
98
|
-
.find(WrappedClass)
|
|
99
|
-
.html()
|
|
100
|
-
.includes('span'),
|
|
101
|
-
).toEqual(false);
|
|
87
|
+
describe('undo functionality', () => {
|
|
88
|
+
it('undo button is disabled when there are no changes', () => {
|
|
89
|
+
render(<Component {...defaultProps} />);
|
|
90
|
+
const undoButton = screen.getByText(/Undo/i).closest('button');
|
|
91
|
+
expect(undoButton).toBeDisabled();
|
|
102
92
|
});
|
|
103
93
|
|
|
104
|
-
it('
|
|
105
|
-
|
|
106
|
-
|
|
94
|
+
it('undo button is enabled after a change', async () => {
|
|
95
|
+
const user = userEvent.setup();
|
|
96
|
+
render(<Component {...defaultProps} />);
|
|
107
97
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
{
|
|
111
|
-
id: 2,
|
|
112
|
-
},
|
|
113
|
-
],
|
|
114
|
-
});
|
|
98
|
+
const addButton = screen.getByTestId('add-item');
|
|
99
|
+
await user.click(addButton);
|
|
115
100
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
id: 2,
|
|
120
|
-
},
|
|
121
|
-
{
|
|
122
|
-
id: 3,
|
|
123
|
-
},
|
|
124
|
-
],
|
|
125
|
-
});
|
|
101
|
+
const undoButton = screen.getByText(/Undo/i).closest('button');
|
|
102
|
+
expect(undoButton).toBeEnabled();
|
|
103
|
+
});
|
|
126
104
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
{
|
|
131
|
-
id: 2,
|
|
132
|
-
},
|
|
133
|
-
],
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
items: [
|
|
137
|
-
{
|
|
138
|
-
id: 2,
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
id: 3,
|
|
142
|
-
},
|
|
143
|
-
],
|
|
144
|
-
},
|
|
145
|
-
]);
|
|
146
|
-
|
|
147
|
-
expect(wrapper.state().session).toEqual({
|
|
148
|
-
items: [
|
|
149
|
-
{
|
|
150
|
-
id: 2,
|
|
151
|
-
},
|
|
152
|
-
{
|
|
153
|
-
id: 3,
|
|
154
|
-
},
|
|
155
|
-
],
|
|
156
|
-
});
|
|
105
|
+
it('undoes changes when undo button is clicked', async () => {
|
|
106
|
+
const user = userEvent.setup();
|
|
107
|
+
const { rerender } = render(<Component {...defaultProps} />);
|
|
157
108
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
{
|
|
162
|
-
items: [
|
|
163
|
-
{
|
|
164
|
-
id: 2,
|
|
165
|
-
},
|
|
166
|
-
],
|
|
167
|
-
},
|
|
168
|
-
]);
|
|
169
|
-
|
|
170
|
-
expect(wrapper.state().session).toEqual({
|
|
171
|
-
items: [
|
|
172
|
-
{
|
|
173
|
-
id: 2,
|
|
174
|
-
},
|
|
175
|
-
],
|
|
176
|
-
});
|
|
109
|
+
// Add an item
|
|
110
|
+
const addButton = screen.getByTestId('add-item');
|
|
111
|
+
await user.click(addButton);
|
|
177
112
|
|
|
178
|
-
|
|
113
|
+
// Get the actual item ID from the onSessionChange call
|
|
114
|
+
const addedItem = onSessionChange.mock.calls[0][0].items[0];
|
|
179
115
|
|
|
180
|
-
|
|
116
|
+
// Update props to reflect the change
|
|
117
|
+
const updatedProps = {
|
|
118
|
+
...defaultProps,
|
|
119
|
+
session: { items: [addedItem] },
|
|
120
|
+
};
|
|
121
|
+
rerender(<Component {...updatedProps} />);
|
|
181
122
|
|
|
182
|
-
expect(
|
|
183
|
-
|
|
184
|
-
|
|
123
|
+
expect(screen.getByTestId(`item-${addedItem.id}`)).toBeInTheDocument();
|
|
124
|
+
|
|
125
|
+
// Click undo
|
|
126
|
+
const undoButton = screen.getByText(/Undo/i).closest('button');
|
|
127
|
+
await user.click(undoButton);
|
|
185
128
|
|
|
186
|
-
|
|
129
|
+
// Should call onSessionChange with previous session
|
|
130
|
+
expect(onSessionChange).toHaveBeenCalledWith({ items: [] });
|
|
187
131
|
});
|
|
132
|
+
});
|
|
188
133
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
134
|
+
describe('reset functionality', () => {
|
|
135
|
+
it('reset button is disabled when there are no changes', () => {
|
|
136
|
+
render(<Component {...defaultProps} />);
|
|
137
|
+
const resetButton = screen.getByText(/Start Over/i).closest('button');
|
|
138
|
+
expect(resetButton).toBeDisabled();
|
|
139
|
+
});
|
|
192
140
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
id: 2,
|
|
197
|
-
},
|
|
198
|
-
],
|
|
199
|
-
});
|
|
141
|
+
it('reset button is enabled after a change', async () => {
|
|
142
|
+
const user = userEvent.setup();
|
|
143
|
+
render(<Component {...defaultProps} />);
|
|
200
144
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
{
|
|
204
|
-
id: 2,
|
|
205
|
-
},
|
|
206
|
-
{
|
|
207
|
-
id: 3,
|
|
208
|
-
},
|
|
209
|
-
],
|
|
210
|
-
});
|
|
145
|
+
const addButton = screen.getByTestId('add-item');
|
|
146
|
+
await user.click(addButton);
|
|
211
147
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
{
|
|
216
|
-
id: 2,
|
|
217
|
-
},
|
|
218
|
-
],
|
|
219
|
-
},
|
|
220
|
-
{
|
|
221
|
-
items: [
|
|
222
|
-
{
|
|
223
|
-
id: 2,
|
|
224
|
-
},
|
|
225
|
-
{
|
|
226
|
-
id: 3,
|
|
227
|
-
},
|
|
228
|
-
],
|
|
229
|
-
},
|
|
230
|
-
]);
|
|
231
|
-
|
|
232
|
-
expect(wrapper.state().session).toEqual({
|
|
233
|
-
items: [
|
|
234
|
-
{
|
|
235
|
-
id: 2,
|
|
236
|
-
},
|
|
237
|
-
{
|
|
238
|
-
id: 3,
|
|
239
|
-
},
|
|
240
|
-
],
|
|
241
|
-
});
|
|
148
|
+
const resetButton = screen.getByText(/Start Over/i).closest('button');
|
|
149
|
+
expect(resetButton).toBeEnabled();
|
|
150
|
+
});
|
|
242
151
|
|
|
243
|
-
|
|
152
|
+
it('resets all changes when reset button is clicked', async () => {
|
|
153
|
+
const user = userEvent.setup();
|
|
154
|
+
const { rerender } = render(<Component {...defaultProps} />);
|
|
244
155
|
|
|
245
|
-
|
|
156
|
+
// Make multiple changes
|
|
157
|
+
const addButton = screen.getByTestId('add-item');
|
|
158
|
+
await user.click(addButton);
|
|
159
|
+
await user.click(addButton);
|
|
246
160
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
161
|
+
// Update props to reflect the changes
|
|
162
|
+
const updatedProps = {
|
|
163
|
+
...defaultProps,
|
|
164
|
+
session: { items: [{ id: 1 }, { id: 2 }] },
|
|
165
|
+
};
|
|
166
|
+
rerender(<Component {...updatedProps} />);
|
|
167
|
+
|
|
168
|
+
// Click reset
|
|
169
|
+
const resetButton = screen.getByText(/Start Over/i).closest('button');
|
|
170
|
+
await user.click(resetButton);
|
|
250
171
|
|
|
251
|
-
|
|
172
|
+
// Should call onSessionChange with initial session
|
|
173
|
+
expect(onSessionChange).toHaveBeenCalledWith({ items: [] });
|
|
252
174
|
});
|
|
253
175
|
});
|
|
254
176
|
});
|