10.1 KB350 lines
Blame
1/**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8import {act, fireEvent, render, screen, waitFor, within} from '@testing-library/react';
9import userEvent from '@testing-library/user-event';
10import {ComparisonType} from 'shared/Comparison';
11import {nullthrows} from 'shared/utils';
12import App from '../App';
13import {ignoreRTL} from '../testQueries';
14import {
15 closeCommitInfoSidebar,
16 COMMIT,
17 expectMessageSentToServer,
18 resetTestMessages,
19 simulateCommits,
20 simulateMessageFromServer,
21 simulateRepoConnected,
22 simulateUncommittedChangedFiles,
23} from '../testUtils';
24
25describe('Shelve', () => {
26 beforeEach(() => {
27 resetTestMessages();
28 render(<App />);
29 act(() => {
30 simulateRepoConnected();
31 closeCommitInfoSidebar();
32 expectMessageSentToServer({
33 type: 'subscribe',
34 kind: 'smartlogCommits',
35 subscriptionID: expect.anything(),
36 });
37 simulateCommits({
38 value: [
39 COMMIT('1', 'some public base', '0', {phase: 'public'}),
40 COMMIT('a', 'My Commit', '1'),
41 COMMIT('b', 'Another Commit', 'a', {isDot: true}),
42 ],
43 });
44
45 simulateUncommittedChangedFiles({
46 value: [
47 {path: 'src/file1.js', status: 'M'},
48 {path: 'src/file2.js', status: 'M'},
49 {path: 'src/file3.js', status: 'M'},
50 ],
51 });
52 });
53 });
54
55 describe('Shelve button', () => {
56 it('Runs shelve when clicking button', async () => {
57 const shelveButton = screen.getByText('Shelve');
58 expect(shelveButton).toBeInTheDocument();
59
60 fireEvent.click(shelveButton);
61 await waitFor(() =>
62 expectMessageSentToServer({
63 type: 'runOperation',
64 operation: expect.objectContaining({
65 args: ['shelve', '--unknown'],
66 }),
67 }),
68 );
69 });
70
71 it('Optimistic state hides all files when shelve running', async () => {
72 expect(screen.queryAllByTestId(/changed-file-src\/file.\.js/)).toHaveLength(3);
73 const shelveButton = screen.getByText('Shelve');
74 fireEvent.click(shelveButton);
75 await waitFor(() =>
76 expect(screen.queryAllByTestId(/changed-file-src\/file.\.js/)).toHaveLength(0),
77 );
78 });
79
80 it('includes name for shelved change if typed', async () => {
81 const shelveButton = screen.getByText('Shelve');
82 expect(shelveButton).toBeInTheDocument();
83
84 const quickInput = screen.getByTestId('quick-commit-title');
85
86 act(() => {
87 userEvent.type(quickInput, 'My Shelf');
88 });
89
90 fireEvent.click(shelveButton);
91 await waitFor(() =>
92 expectMessageSentToServer({
93 type: 'runOperation',
94 operation: expect.objectContaining({
95 args: ['shelve', '--unknown', '--name', 'My Shelf'],
96 }),
97 }),
98 );
99 });
100
101 it('only shelves selected files', async () => {
102 const shelveButton = screen.getByText('Shelve');
103 expect(shelveButton).toBeInTheDocument();
104
105 // uncheck one file
106 fireEvent.click(
107 nullthrows(
108 screen.getByTestId('changed-file-src/file2.js').querySelector('input[type=checkbox]'),
109 ),
110 );
111
112 fireEvent.click(shelveButton);
113
114 await waitFor(() =>
115 expectMessageSentToServer({
116 type: 'runOperation',
117 operation: expect.objectContaining({
118 args: [
119 'shelve',
120 '--unknown',
121 {type: 'repo-relative-file', path: 'src/file1.js'},
122 {type: 'repo-relative-file', path: 'src/file3.js'},
123 ],
124 }),
125 }),
126 );
127 });
128
129 it('Optimistic state hides selected files when shelve running', async () => {
130 expect(screen.queryAllByTestId(/src\/file.\.js/)).toHaveLength(3);
131 const shelveButton = screen.getByText('Shelve');
132 fireEvent.click(shelveButton);
133 await waitFor(() => expect(screen.queryAllByTestId(/src\/file.\.js/)).toHaveLength(0));
134 });
135
136 it('shelve button disabled if no files selected', async () => {
137 fireEvent.click(screen.getByText('Deselect All'));
138
139 const shelveButton = screen.getByText('Shelve');
140 await waitFor(() => {
141 expect(shelveButton).toBeInTheDocument();
142 expect(shelveButton).toBeDisabled();
143 });
144 });
145 });
146
147 describe('Shelved changes list', () => {
148 const SHELVES = [
149 {
150 hash: 'aaa',
151 name: 'my shelve',
152 date: new Date(),
153 description: 'shelved on commit b',
154 filesSample: [{path: 'src/shelvedfile.js', status: 'M' as const}],
155 totalFileCount: 1,
156 },
157 ];
158
159 it('shows shelved changes list tooltip', () => {
160 fireEvent.click(screen.getByTestId('shelved-changes-button'));
161 expect(screen.getByText('Shelved Changes')).toBeInTheDocument();
162
163 expectMessageSentToServer({
164 type: 'fetchShelvedChanges',
165 });
166 });
167
168 it('renders errors from fetching shelved changes', async () => {
169 fireEvent.click(screen.getByTestId('shelved-changes-button'));
170 act(() => {
171 simulateMessageFromServer({
172 type: 'fetchedShelvedChanges',
173 shelvedChanges: {
174 error: new Error('failed to fetch shelved changes'),
175 },
176 });
177 });
178
179 await waitFor(() => {
180 expect(
181 within(screen.getByTestId('shelved-changes-dropdown')).getByText(
182 'Could not fetch shelved changes',
183 ),
184 ).toBeInTheDocument();
185 });
186 });
187
188 it('renders empty state', async () => {
189 fireEvent.click(screen.getByTestId('shelved-changes-button'));
190 act(() => {
191 simulateMessageFromServer({
192 type: 'fetchedShelvedChanges',
193 shelvedChanges: {
194 value: [],
195 },
196 });
197 });
198
199 await waitFor(() => {
200 expect(
201 within(screen.getByTestId('shelved-changes-dropdown')).getByText('No shelved changes'),
202 ).toBeInTheDocument();
203 });
204 });
205
206 describe('with shelved change rendered', () => {
207 beforeEach(async () => {
208 fireEvent.click(screen.getByTestId('shelved-changes-button'));
209 act(() => {
210 simulateMessageFromServer({
211 type: 'fetchedShelvedChanges',
212 shelvedChanges: {
213 value: SHELVES,
214 },
215 });
216 });
217
218 await waitFor(() => {
219 expect(
220 within(screen.getByTestId('shelved-changes-dropdown')).getByText('my shelve'),
221 ).toBeInTheDocument();
222 });
223 });
224
225 it('renders shelved changes', () => {
226 expect(
227 within(screen.getByTestId('shelved-changes-dropdown')).getByText(
228 ignoreRTL('shelvedfile.js'),
229 ),
230 ).toBeInTheDocument();
231 });
232
233 it('runs unshelve', () => {
234 fireEvent.click(screen.getByText('Unshelve'));
235
236 expectMessageSentToServer({
237 type: 'runOperation',
238 operation: expect.objectContaining({
239 args: ['unshelve', '--name', 'my shelve'],
240 }),
241 });
242 });
243
244 it('runs apply', () => {
245 fireEvent.click(screen.getByText('Apply'));
246
247 expectMessageSentToServer({
248 type: 'runOperation',
249 operation: expect.objectContaining({
250 args: ['unshelve', '--keep', '--name', 'my shelve'],
251 }),
252 });
253 });
254
255 it('runs delete', () => {
256 fireEvent.click(screen.getByTestId('delete-shelve-aaa'));
257
258 expectMessageSentToServer({
259 type: 'runOperation',
260 operation: expect.objectContaining({
261 args: ['shelve', '--delete', 'my shelve'],
262 }),
263 });
264 });
265
266 it('dismisses tooltip while running unshelve', () => {
267 fireEvent.click(screen.getByText('Unshelve'));
268
269 expect(screen.queryByTestId('shelved-changes-dropdown')).not.toBeInTheDocument();
270 });
271
272 it('can open comparison view for the shelve', async () => {
273 fireEvent.click(
274 within(screen.getByTestId('shelved-changes-dropdown')).getByText('View Changes'),
275 );
276
277 await waitFor(() => {
278 expectMessageSentToServer({
279 type: 'requestComparison',
280 comparison: {
281 type: ComparisonType.Committed,
282 hash: 'aaa',
283 },
284 });
285 });
286 });
287
288 it('refetches shelves when reopening dropdown', async () => {
289 expect(
290 within(screen.getByTestId('shelved-changes-dropdown')).getByText('my shelve'),
291 ).toBeInTheDocument();
292
293 resetTestMessages();
294
295 act(() => {
296 userEvent.type(document.body, '{Escape}');
297 });
298
299 // reopen the dropdown
300 fireEvent.click(screen.getByTestId('shelved-changes-button'));
301 expectMessageSentToServer({
302 type: 'fetchShelvedChanges',
303 });
304
305 // send a new set of changes
306 const NEW_SHELVES = [
307 {
308 hash: 'a',
309 name: 'my new shelve',
310 date: new Date(),
311 description: 'shelved on commit b',
312 filesSample: [{path: 'src/shelvedfile.js', status: 'M' as const}],
313 totalFileCount: 1,
314 },
315 ];
316 act(() => {
317 simulateMessageFromServer({
318 type: 'fetchedShelvedChanges',
319 shelvedChanges: {
320 value: NEW_SHELVES,
321 },
322 });
323 });
324
325 // make sure we get the new name and not the old one
326 expect(
327 within(screen.getByTestId('shelved-changes-dropdown')).queryByText('my shelve'),
328 ).not.toBeInTheDocument();
329 await waitFor(() => {
330 expect(
331 within(screen.getByTestId('shelved-changes-dropdown')).getByText('my new shelve'),
332 ).toBeInTheDocument();
333 });
334 });
335
336 it('shows optimistic state for unshelve', () => {
337 expect(
338 within(screen.getByTestId('commit-tree-root')).queryByText(ignoreRTL('shelvedfile.js')),
339 ).not.toBeInTheDocument();
340
341 fireEvent.click(screen.getByText('Unshelve'));
342
343 expect(
344 within(screen.getByTestId('commit-tree-root')).getByText(ignoreRTL('shelvedfile.js')),
345 ).toBeInTheDocument();
346 });
347 });
348 });
349});
350