8.5 KB268 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 type {Hash} from './types';
9
10import {act, fireEvent, screen, waitFor, within} from '@testing-library/react';
11import {nullthrows} from 'shared/utils';
12import {commitMessageFieldsSchema} from './CommitInfoView/CommitMessageFields';
13import {OSSCommitMessageFieldSchema} from './CommitInfoView/OSSCommitMessageFieldsSchema';
14import {convertFieldNameToKey} from './CommitInfoView/utils';
15import {readAtom} from './jotaiUtils';
16import {individualToggleKey} from './selection';
17import {expectMessageSentToServer} from './testUtils';
18import {assert} from './utils';
19
20export const CommitTreeListTestUtils = {
21 withinCommitTree() {
22 return within(screen.getByTestId('commit-tree-root'));
23 },
24
25 async clickGoto(commit: Hash) {
26 const myCommit = screen.queryByTestId(`commit-${commit}`);
27 const gotoButton = myCommit?.querySelector('.goto-button button');
28 expect(gotoButton).toBeDefined();
29 await act(async () => {
30 await fireEvent.click(gotoButton as Element);
31 });
32 },
33};
34
35export const CommitInfoTestUtils = {
36 withinCommitInfo() {
37 return within(screen.getByTestId('commit-info-view'));
38 },
39
40 withinCommitActionBar() {
41 return within(screen.getByTestId('commit-info-actions-bar'));
42 },
43
44 openCommitInfoSidebar() {
45 screen.queryAllByTestId('drawer-label').forEach(el => {
46 const commitInfoTab = within(el).queryByText('Commit Info');
47 commitInfoTab?.click();
48 });
49 },
50
51 clickToSelectCommit(hash: string, cmdClick?: boolean) {
52 const commit = within(screen.getByTestId(`commit-${hash}`)).queryByTestId('draggable-commit');
53 expect(commit).toBeInTheDocument();
54 act(() => {
55 fireEvent.click(nullthrows(commit), {[individualToggleKey]: cmdClick === true});
56 });
57 },
58
59 clickCommitMode() {
60 const commitRadioChoice = within(screen.getByTestId('commit-info-toolbar-top')).getByText(
61 'Commit',
62 );
63 act(() => {
64 fireEvent.click(commitRadioChoice);
65 });
66 },
67
68 clickAmendMode() {
69 const commitRadioChoice = within(screen.getByTestId('commit-info-toolbar-top')).getByText(
70 'Amend',
71 );
72 act(() => {
73 fireEvent.click(commitRadioChoice);
74 });
75 },
76
77 async clickAmendButton() {
78 const amendButton: HTMLButtonElement | null = within(
79 screen.getByTestId('commit-info-actions-bar'),
80 ).queryByText('Amend');
81 expect(amendButton).toBeInTheDocument();
82 act(() => {
83 fireEvent.click(nullthrows(amendButton));
84 });
85 await waitFor(() =>
86 expectMessageSentToServer({
87 type: 'runOperation',
88 operation: expect.objectContaining({
89 args: expect.arrayContaining(['amend']),
90 }),
91 }),
92 );
93 },
94
95 clickAmendMessageButton() {
96 const amendMessageButton: HTMLButtonElement | null = within(
97 screen.getByTestId('commit-info-actions-bar'),
98 ).queryByText('Amend Message');
99 expect(amendMessageButton).toBeInTheDocument();
100 act(() => {
101 fireEvent.click(nullthrows(amendMessageButton));
102 });
103 },
104
105 async clickCommitButton() {
106 const commitButton: HTMLButtonElement | null = within(
107 screen.getByTestId('commit-info-actions-bar'),
108 ).queryByText('Commit');
109 expect(commitButton).toBeInTheDocument();
110 act(() => {
111 fireEvent.click(nullthrows(commitButton));
112 });
113 await waitFor(() =>
114 expectMessageSentToServer({
115 type: 'runOperation',
116 operation: expect.objectContaining({
117 args: expect.arrayContaining(['commit']),
118 }),
119 }),
120 );
121 },
122
123 clickCancel() {
124 const cancelButton: HTMLButtonElement | null =
125 CommitInfoTestUtils.withinCommitInfo().queryByText('Cancel');
126 expect(cancelButton).toBeInTheDocument();
127
128 act(() => {
129 fireEvent.click(nullthrows(cancelButton));
130 });
131 },
132
133 /** Get the textarea for the title editor */
134 getTitleEditor(): HTMLTextAreaElement {
135 const title = screen.getByTestId('commit-info-title-field') as HTMLTextAreaElement;
136 expect(title).toBeInTheDocument();
137 return title;
138 },
139
140 /** Get the textarea for the description editor
141 * For internal builds, this points to the "summary" editor instead of the "description" editor
142 */
143 getDescriptionEditor(): HTMLTextAreaElement {
144 const description = screen.getByTestId(
145 isInternalMessageFields() ? 'commit-info-summary-field' : 'commit-info-description-field',
146 ) as HTMLTextAreaElement;
147 expect(description).toBeInTheDocument();
148 return description;
149 },
150
151 /** Get the textarea for the test plan editor. Unavailable in OSS tests (use internal-only tests). */
152 getTestPlanEditor(): HTMLTextAreaElement {
153 assert(isInternalMessageFields(), 'Cannot edit test plan in OSS');
154 const testPlan = screen.getByTestId('commit-info-test-plan-field') as HTMLTextAreaElement;
155 expect(testPlan).toBeInTheDocument();
156 return testPlan;
157 },
158
159 /** Get the input element for a given field's editor, according to the field key in the FieldConfig (actually just a div in tests) */
160 getFieldEditor(key: string): HTMLDivElement {
161 const renderKey = convertFieldNameToKey(key);
162 const el = screen.getByTestId(`commit-info-${renderKey}-field`) as HTMLDivElement;
163 expect(el).toBeInTheDocument();
164 return el;
165 },
166
167 descriptionTextContent() {
168 return CommitInfoTestUtils.getDescriptionEditor().value;
169 },
170
171 clickToEditTitle() {
172 act(() => {
173 const title = screen.getByTestId('commit-info-rendered-title');
174 expect(title).toBeInTheDocument();
175 fireEvent.click(title);
176 });
177 },
178 clickToEditDescription() {
179 act(() => {
180 const description = screen.getByTestId(
181 isInternalMessageFields()
182 ? 'commit-info-rendered-summary'
183 : 'commit-info-rendered-description',
184 );
185 expect(description).toBeInTheDocument();
186 fireEvent.click(description);
187 });
188 },
189 clickToEditTestPlan() {
190 assert(isInternalMessageFields(), 'Cannot edit test plan in OSS');
191 act(() => {
192 const testPlan = screen.getByTestId('commit-info-rendered-test-plan');
193 expect(testPlan).toBeInTheDocument();
194 fireEvent.click(testPlan);
195 });
196 },
197
198 /** Internal tests only, since GitHub's message schema does not include this field */
199 clickToEditReviewers() {
200 act(() => {
201 const title = screen.getByTestId('commit-info-rendered-reviewers');
202 expect(title).toBeInTheDocument();
203 fireEvent.click(title);
204 });
205 },
206
207 expectIsEditingTitle() {
208 const titleEditor = screen.queryByTestId('commit-info-title-field') as HTMLInputElement;
209 expect(titleEditor).toBeInTheDocument();
210 },
211 expectIsNOTEditingTitle() {
212 const titleEditor = screen.queryByTestId('commit-info-title-field') as HTMLInputElement;
213 expect(titleEditor).not.toBeInTheDocument();
214 },
215
216 expectIsEditingDescription() {
217 const descriptionEditor = screen.queryByTestId(
218 isInternalMessageFields() ? 'commit-info-summary-field' : 'commit-info-description-field',
219 ) as HTMLTextAreaElement;
220 expect(descriptionEditor).toBeInTheDocument();
221 },
222 expectIsNOTEditingDescription() {
223 const descriptionEditor = screen.queryByTestId(
224 isInternalMessageFields() ? 'commit-info-summary-field' : 'commit-info-description-field',
225 ) as HTMLTextAreaElement;
226 expect(descriptionEditor).not.toBeInTheDocument();
227 },
228};
229
230export const MergeConflictTestUtils = {
231 waitForContinueButtonNotDisabled: () =>
232 waitFor(() =>
233 expect(
234 within(screen.getByTestId('commit-tree-root')).getByTestId(
235 'conflict-continue-button',
236 ) as HTMLButtonElement,
237 ).not.toBeDisabled(),
238 ),
239 clickContinueConflicts: () =>
240 act(() => {
241 fireEvent.click(
242 within(screen.getByTestId('commit-tree-root')).getByTestId('conflict-continue-button'),
243 );
244 }),
245 expectInMergeConflicts: () =>
246 expect(
247 within(screen.getByTestId('commit-tree-root')).getByText('Unresolved Merge Conflicts'),
248 ).toBeInTheDocument(),
249 expectNotInMergeConflicts: () =>
250 expect(
251 within(screen.getByTestId('commit-tree-root')).queryByText('Unresolved Merge Conflicts'),
252 ).not.toBeInTheDocument(),
253};
254
255function isInternalMessageFields(): boolean {
256 const schema = readAtom(commitMessageFieldsSchema);
257 return schema !== OSSCommitMessageFieldSchema;
258}
259
260/**
261 * When querying changed files, there may be unicode left-to-right marks in the path,
262 * which make the test hard to read. This util searches for a string, inserting optional
263 * RTL marks at path boundaries.
264 */
265export function ignoreRTL(s: string): RegExp {
266 return new RegExp(`^\u200E?${s}\u200E?$`);
267}
268