Skip to content

Commit 539e15e

Browse files
UI - draft component test cases fixed
1 parent 62a2cc1 commit 539e15e

File tree

2 files changed

+210
-131
lines changed

2 files changed

+210
-131
lines changed
Lines changed: 194 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,83 @@
1-
import React from 'react'
2-
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
3-
import { MemoryRouter } from 'react-router-dom'
1+
import { render, screen, fireEvent, act, waitFor } from '@testing-library/react'
2+
import { BrowserRouter } from 'react-router-dom'
43
import { AppStateContext } from '../../state/AppProvider'
54
import Draft from './Draft'
5+
import { Section } from '../../api/models'
66
import { saveAs } from 'file-saver'
7+
import { defaultMockState } from '../../test/test.utils';
8+
import { MemoryRouter } from 'react-router-dom';
9+
10+
import { Document, Packer, Paragraph, TextRun } from 'docx'
711

8-
// Mock the SectionCard component
9-
jest.mock('../../components/DraftCards/SectionCard', () => () => (
10-
<div data-testid="mock-section-card">Mock Section Card</div>
11-
))
1212

13+
// Mocks for third-party components and modules
1314
jest.mock('file-saver', () => ({
1415
saveAs: jest.fn(),
16+
}))
17+
18+
jest.mock('../../components/DraftCards/TitleCard', () => ({ children }: { children: React.ReactNode }) => (
19+
<div data-testid="title-card">
20+
Title Card
21+
{children}
22+
</div>
23+
))
24+
25+
jest.mock('../../components/DraftCards/SectionCard', () => ({
26+
__esModule: true,
27+
default: ({ sectionIdx }: { sectionIdx: number }) => (
28+
<div data-testid={`section-card-${sectionIdx}`}>Section {sectionIdx}</div>
29+
),
30+
}))
31+
32+
// Mock CommandBarButton from Fluent UI
33+
jest.mock('@fluentui/react', () => ({
34+
...jest.requireActual('@fluentui/react'),
35+
CommandBarButton: ({ onClick, disabled, text, ariaLabel, iconProps }: any) => (
36+
<button
37+
aria-label={ariaLabel}
38+
onClick={onClick}
39+
disabled={disabled}
40+
data-testid="command-bar-button">
41+
{iconProps?.iconName && <span>{iconProps.iconName}</span>}
42+
{text}
43+
</button>
44+
),
1545
}));
1646

17-
// jest.mock('../../components/DraftCards/TitleCard', () => () => <div data-testid="mock-title-card">Mock Title Card</div>)
47+
jest.mock('react-router-dom', () => ({
48+
...jest.requireActual('react-router-dom'),
49+
useNavigate: jest.fn(),
50+
}))
51+
52+
jest.mock('docx', () => ({
53+
Document: jest.fn().mockImplementation((options) => {
54+
return {
55+
sections: options.sections || [],
56+
}
57+
}),
58+
Packer: {
59+
toBlob: jest.fn().mockResolvedValue(new Blob()), // Mock the toBlob method
60+
},
61+
Paragraph: jest.fn(),
62+
TextRun: jest.fn(),
63+
}))
64+
1865
const mockAppState = {
19-
state: {
20-
draftedDocument: {
21-
sections: [
22-
{ title: 'Section 1', content: 'Content of section 1.' },
23-
{ title: 'Section 2', content: 'Content of section 2.' }
24-
]
25-
},
26-
draftedDocumentTitle: 'Sample Draft'
66+
draftedDocument: {
67+
sections: [
68+
{ title: 'Section 1', content: 'Content of section 1.' },
69+
{ title: 'Section 2', content: 'Content of section 2.' }
70+
]
2771
},
28-
dispatch: jest.fn()
72+
isLoadedSections: [],
73+
draftedDocumentTitle: 'Sample Draft'
2974
}
3075

76+
3177
const renderComponent = (appState: any) => {
3278
return render(
3379
<MemoryRouter>
34-
<AppStateContext.Provider value={appState}>
80+
<AppStateContext.Provider value={{ state: appState, dispatch: jest.fn() }}>
3581
<Draft />
3682
</AppStateContext.Provider>
3783
</MemoryRouter>
@@ -41,139 +87,183 @@ const renderComponent = (appState: any) => {
4187
describe('Draft Component', () => {
4288
beforeEach(() => {
4389
jest.clearAllMocks()
90+
4491
})
4592

46-
// it('renders title card and section cards', () => {
47-
// renderComponent(mockAppState)
4893

49-
// expect(screen.getByTestId('mock-title-card')).toBeInTheDocument()
50-
// expect(screen.getByText('Section 1')).toBeInTheDocument()
51-
// expect(screen.getByText('Content of section 1.')).toBeInTheDocument()
52-
// expect(screen.getByText('Section 2')).toBeInTheDocument()
53-
// expect(screen.getByText('Content of section 2.')).toBeInTheDocument()
54-
// })
94+
test('renders TitleCard and SectionCards correctly', async () => {
95+
renderComponent(mockAppState)
5596

56-
it('redirects to home page if draftedDocument is empty', () => {
57-
const appStateWithEmptyDraft = {
58-
...mockAppState,
59-
state: {
60-
...mockAppState.state,
61-
draftedDocument: null
62-
}
63-
}
97+
expect(screen.getByTestId('title-card')).toBeInTheDocument()
6498

65-
renderComponent(appStateWithEmptyDraft)
66-
expect(window.location.pathname).toBe('/')
67-
})
99+
await waitFor(() => {
100+
expect(screen.getByTestId('section-card-0')).toBeInTheDocument()
101+
expect(screen.getByTestId('section-card-1')).toBeInTheDocument()
102+
})
68103

69-
it('sanitizes title correctly', () => {
70-
const sanitizedTitle = mockAppState.state.draftedDocumentTitle.replace(/[^a-zA-Z0-9]/g, '')
71-
expect(sanitizedTitle).toBe('SampleDraft')
72-
})
73-
74-
it('exports document when export button is clicked', async () => {
75-
const { saveAs } = require('file-saver');
76104

77-
renderComponent(mockAppState)
105+
})
78106

79-
fireEvent.click(screen.getByRole('button', { name: /Export Document/i }))
107+
test('disables the export button when sections are not loaded', async () => {
108+
const mockStateWithIncompleteLoad = {
109+
...mockAppState,
110+
isLoadedSections: [{ title: 'Section 1', content: 'Content of section 1' }], // One section not loaded
111+
}
112+
renderComponent(mockStateWithIncompleteLoad)
80113

81114
await waitFor(() => {
82-
saveAs(new Blob(['test content']), 'DraftTemplate-SampleDraft.docx');
83-
expect(saveAs).toHaveBeenCalledWith(expect.any(Blob), 'DraftTemplate-SampleDraft.docx')
115+
const exportButton = screen.getByTestId('command-bar-button')
116+
expect(exportButton).toBeDisabled()
84117
})
118+
85119
})
86-
87120

88-
test('renders empty string when draftedDocumentTitle is an empty string', () => {
89-
const appStateWithEmptyTitle = {
121+
test('enabled the export button when sections are loaded', async () => {
122+
const mockStateWithIncompleteLoad = {
90123
...mockAppState,
91-
state: {
92-
...mockAppState.state,
93-
draftedDocumentTitle: ''
94-
}
124+
isLoadedSections: [{ title: 'Section 1', content: 'Content of section 1' },
125+
{ title: 'Section 2', content: 'Content of section 2.' }
126+
],
95127
}
128+
renderComponent(mockStateWithIncompleteLoad)
129+
await waitFor(() => {
130+
const exportButton = screen.getByTestId('command-bar-button')
131+
expect(exportButton).toBeEnabled()
132+
})
96133

97-
renderComponent(appStateWithEmptyTitle)
98-
99-
expect(screen.getByDisplayValue('')).toBeInTheDocument()
100134
})
101135

102-
test('renders empty string when draftedDocumentTitle is null', () => {
103-
const appStateWithNullTitle = {
136+
test('triggers exportToWord function when export button is clicked', async () => {
137+
const mockStateWithIncompleteLoad = {
104138
...mockAppState,
105-
state: {
106-
...mockAppState.state,
107-
draftedDocumentTitle: null
108-
}
139+
isLoadedSections: [{ title: 'Section 1', content: 'Content of section 1' },
140+
{ title: 'Section 2', content: 'Content of section 2.' }
141+
],
109142
}
143+
renderComponent(mockStateWithIncompleteLoad)
110144

111-
renderComponent(appStateWithNullTitle)
145+
await waitFor(async () => {
112146

113-
expect(screen.getByDisplayValue('')).toBeInTheDocument()
114-
})
147+
const exportButton = screen.getByText(/Export Document/i)
115148

116-
test('returns draftedDocumentTitle when it is a valid string', () => {
117-
renderComponent(mockAppState)
149+
await act(async () => {
150+
fireEvent.click(exportButton)
151+
})
118152

119-
expect(screen.getByDisplayValue('Sample Draft')).toBeInTheDocument()
153+
expect(saveAs).toHaveBeenCalled()
154+
});
120155
})
121156

122-
test('does not crash when draftedDocument is null', () => {
123-
const appStateWithNullDocument = {
157+
test('does not render any SectionCard when sections array is empty', async () => {
158+
const mockStateWithIncompleteLoad = {
124159
...mockAppState,
125-
state: {
126-
...mockAppState.state,
127-
draftedDocument: null
160+
draftedDocument: {
161+
sections: []
128162
}
129163
}
164+
renderComponent(mockStateWithIncompleteLoad)
165+
166+
await waitFor(() => {
167+
const sectionCards = screen.queryAllByTestId(/^section-card-/)
168+
expect(sectionCards).toHaveLength(0)
169+
});
170+
})
130171

131-
renderComponent(appStateWithNullDocument)
172+
test('does not render any SectionCard when sections array is null', async () => {
173+
const mockStateWithIncompleteLoad = {
174+
...mockAppState,
175+
draftedDocument: {
176+
sections: null
177+
}
178+
}
179+
renderComponent(mockStateWithIncompleteLoad)
132180

133-
expect(screen.queryByText('Section 1')).not.toBeInTheDocument()
134-
expect(screen.queryByText('Section 2')).not.toBeInTheDocument()
181+
await waitFor(() => {
182+
const sectionCards = screen.queryAllByTestId(/^section-card-/)
183+
expect(sectionCards).toHaveLength(0)
184+
});
135185
})
136186

137-
test('does not crash when appStateContext is undefined', () => {
138-
const appStateWithUndefinedContext = {
139-
state: {}
187+
test('redirects to home page when draftedDocument is empty', async () => {
188+
const mockStateEmptyDoc = {
189+
...mockAppState,
190+
draftedDocument: null,
191+
sections: [],
192+
isLoadedSections: [],
193+
draftedDocumentTitle: null,
140194
}
141195

142-
renderComponent(appStateWithUndefinedContext)
196+
const mockNavigate = jest.fn()
197+
jest.spyOn(require('react-router-dom'), 'useNavigate').mockReturnValue(mockNavigate)
198+
199+
renderComponent(mockStateEmptyDoc)
200+
201+
await waitFor(() => {
202+
expect(mockNavigate).toHaveBeenCalledWith('/')
203+
})
143204

144-
expect(screen.getByDisplayValue('')).toBeInTheDocument()
145205
})
146206

147-
test('does not render any SectionCard when sections array is empty', () => {
148-
const appStateWithEmptySections = {
207+
test('does not call saveAs if export button is disabled', async () => {
208+
const mockStateWithSectionsNotLoaded = {
149209
...mockAppState,
150-
state: {
151-
...mockAppState.state,
152-
draftedDocument: {
153-
sections: []
154-
}
155-
}
210+
isLoadedSections: [], // Sections are not loaded
156211
}
157212

158-
renderComponent(appStateWithEmptySections)
213+
renderComponent(mockStateWithSectionsNotLoaded)
214+
215+
const exportButton = screen.getByText(/Export Document/i)
159216

160-
const sectionCards = screen.queryAllByTestId('mock-section-card')
161-
expect(sectionCards.length).toBe(0)
217+
// Ensure the button is disabled and clicking it won't trigger export
218+
expect(exportButton).toBeDisabled()
219+
220+
await act(async () => {
221+
fireEvent.click(exportButton)
222+
})
223+
224+
expect(saveAs).not.toHaveBeenCalled()
162225
})
163226

164-
test('renders SectionCard for each section in draftedDocument', async() => {
165-
renderComponent(mockAppState)
227+
test('calls saveAs when exportToWord is triggered', async () => {
166228

167-
await waitFor(() => {
168-
const sectionCards = screen.getAllByTestId('mock-section-card');
169-
expect(sectionCards.length).toBe(mockAppState.state.draftedDocument.sections.length);
170-
});
229+
const mockStateWithIncompleteLoad = {
230+
...mockAppState,
231+
isLoadedSections: [{ title: 'Section 1', content: 'Content of section 1' },
232+
{ title: 'Section 2', content: 'Content of section 2.' }
233+
], // One section not loaded
234+
}
235+
renderComponent(mockStateWithIncompleteLoad)
236+
237+
await waitFor(async () => {
238+
239+
const exportButton = screen.getByText(/Export Document/i)
240+
241+
fireEvent.click(exportButton)
242+
//expect(Packer.toBlob).toHaveBeenCalledTimes(1)
243+
await waitFor(() => {
244+
expect(saveAs).toHaveBeenCalled()
245+
})
246+
});
171247
})
172248

173-
test('getTitle function returns correct title when draftedDocumentTitle is valid', () => {
174-
renderComponent(mockAppState)
175-
expect(screen.getByDisplayValue('Sample Draft')).toBeInTheDocument()
249+
250+
test('generate document when draftedDocumentTitle is null', async () => {
251+
const mockStateWithIncompleteLoad = {
252+
...mockAppState,
253+
isLoadedSections: [{ title: 'Section 1', content: 'Content of section 1' },
254+
{ title: 'Section 2', content: 'Content of section 2.' }
255+
],
256+
draftedDocumentTitle: null
257+
}
258+
renderComponent(mockStateWithIncompleteLoad)
259+
await waitFor(async () => {
260+
const exportButton = screen.getByText(/Export Document/i)
261+
fireEvent.click(exportButton)
262+
263+
await waitFor(() => {
264+
expect(Document).toHaveBeenCalledTimes(1)
265+
})
266+
});
176267
})
177268

178-
179269
})

0 commit comments

Comments
 (0)