-
Notifications
You must be signed in to change notification settings - Fork 481
Expand file tree
/
Copy pathBottomBox.test.tsx
More file actions
240 lines (200 loc) · 8.27 KB
/
Copy pathBottomBox.test.tsx
File metadata and controls
240 lines (200 loc) · 8.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import { Provider } from 'react-redux';
import { stripIndent } from 'common-tags';
import { ProfileViewer } from 'firefox-profiler/components/app/ProfileViewer';
import { updateUrlState } from 'firefox-profiler/actions/app';
import { viewProfile } from 'firefox-profiler/actions/receive-profile';
import { stateFromLocation } from 'firefox-profiler/app-logic/url-handling';
import { ensureExists } from 'firefox-profiler/utils/types';
import {
render,
screen,
within,
} from 'firefox-profiler/test/fixtures/testing-library';
import { blankStore } from 'firefox-profiler/test/fixtures/stores';
import { getProfileFromTextSamples } from 'firefox-profiler/test/fixtures/profiles/processed-profile';
import { fireFullClick } from 'firefox-profiler/test/fixtures/utils';
import { autoMockDomRect } from 'firefox-profiler/test/fixtures/mocks/domrect';
// We're not interested in the timeline in this test
jest.mock('../../components/timeline', () => ({
Timeline: 'custom-timeline',
}));
describe('BottomBox', () => {
autoMockDomRect();
afterEach(() => {
// @ts-expect-error TODO: Our tsconfig includes "DOM" in compilerOptions.lib;
// maybe tests should not have it in there and instead use jsdom's types?
delete Range.prototype.getClientRects;
});
function setup() {
// @ts-expect-error see above
if (Range.prototype.getClientRects) {
throw new Error(
'jsdom now implements Range.prototype.getClientRects, please update this test.'
);
}
// @codemirror/view uses getClientRects when scrolling.
// @ts-expect-error TS2322: Type '() => DOMRect[]' is not assignable to type '() => DOMRectList'.
Range.prototype.getClientRects = () => {
return [new DOMRect(0, 0, 1024, 768)];
};
const revision = '997f00815e6bc28806b75448c8829f0259d2cb28';
const filepath = 'widget/cocoa/nsAppShell.mm';
window.fetchMock
.post('http://127.0.0.1:8000/source/v1', 500)
.get(
`https://hg.mozilla.org/mozilla-central/raw-file/${revision}/${filepath}`,
stripIndent`
line 1
line 2
line 3
line 4
line 5
line 6
line 7
`
)
.post(
'http://127.0.0.1:8000/asm/v1',
JSON.stringify({
startAddress: '0x20',
size: '0x1a',
arch: 'x86_64',
syntax: ['Intel'],
instructions: [
[0, 'push rsi'],
[1, 'push rdi'],
[2, 'push rbx'],
[3, 'sub rsp, 0x20'],
[7, 'mov rsi, rcx'],
[10, 'mov rdi, qword [rcx + 0x58]'],
[14, 'mov ebx, edi'],
[16, 'and ebx, 0x1400'],
[22, 'call 0x16c5d35'],
],
})
);
const { profile } = getProfileFromTextSamples(`
A[file:hg:hg.mozilla.org/mozilla-central:${filepath}:${revision}][line:4][address:30][sym:Asym:20:1a][lib:libA.so] A[file:hg:hg.mozilla.org/mozilla-central:${filepath}:${revision}][line:4][address:70][sym:A2sym:60:1a][lib:libA.so]
B[file:git:github.com/rust-lang/rust:library/std/src/sys/unix/thread.rs:53cb7b09b00cbea8754ffb78e7e3cb521cb8af4b]
C[lib:libC.so][file:s3:gecko-generated-sources:a5d3747707d6877b0e5cb0a364e3cb9fea8aa4feb6ead138952c2ba46d41045297286385f0e0470146f49403e46bd266e654dfca986de48c230f3a71c2aafed4/ipc/ipdl/PBackgroundChild.cpp:]
D[lib:libD.so]
`);
const store = blankStore();
store.dispatch(
updateUrlState(
stateFromLocation({
pathname: '/from-browser',
search: '?symbolServer=http://127.0.0.1:8000',
hash: '',
})
)
);
store.dispatch(viewProfile(profile));
render(
<Provider store={store}>
<ProfileViewer />
</Provider>
);
return {
sourceView: () => document.querySelector('.sourceView') as HTMLElement,
assemblyView: () =>
document.querySelector('.assemblyView') as HTMLElement,
};
}
it('does not show the source view at loadtime', () => {
const { sourceView, assemblyView } = setup();
expect(sourceView()).not.toBeInTheDocument();
expect(assemblyView()).not.toBeInTheDocument();
});
it('should show the source view when a line in the tree view is double-clicked', async () => {
const { sourceView, assemblyView } = setup();
const frameElement = screen.getByRole('treeitem', { name: /^A/ });
fireFullClick(frameElement);
fireFullClick(frameElement, { detail: 2 });
expect(sourceView()).toBeInTheDocument();
expect(assemblyView()).not.toBeInTheDocument();
const sourceViewElement = ensureExists(sourceView());
const sourceViewContent =
await within(sourceViewElement).findByRole('textbox');
// Verify source content is rendered. Syntax highlighting splits tokens
// across spans, so we match against each cm-line's full textContent.
await within(sourceViewContent).findAllByText('line');
expect(sourceViewContent).toMatchSnapshot();
});
it('should show the assembly view when pressing the toggle button', async () => {
const { sourceView, assemblyView } = setup();
const frameElement = screen.getByRole('treeitem', { name: /^A/ });
fireFullClick(frameElement);
fireFullClick(frameElement, { detail: 2 });
expect(sourceView()).toBeInTheDocument();
expect(assemblyView()).not.toBeInTheDocument();
const asmViewShowButton = ensureExists(
document.querySelector('.bottom-assembly-button')
) as HTMLElement;
fireFullClick(asmViewShowButton);
expect(sourceView()).toBeInTheDocument();
expect(assemblyView()).toBeInTheDocument();
const assemblyViewElement = ensureExists(assemblyView());
const assemblyViewContent =
await within(assemblyViewElement).findByRole('textbox');
// Find one of the instructions. Once we have assembly syntax highlighting,
// we'll probably have to match on a smaller string.
await within(assemblyViewContent).findAllByText(
'mov rdi, qword [rcx + 0x58]'
);
expect(assemblyViewContent).toMatchSnapshot();
// Click the toggle button again and make sure the assembly view hides.
const asmViewHideButton = ensureExists(
document.querySelector('.bottom-assembly-button')
) as HTMLElement;
fireFullClick(asmViewHideButton);
expect(assemblyView()).not.toBeInTheDocument();
});
it('should navigate between symbols using prev/next buttons', async () => {
const { sourceView, assemblyView } = setup();
const frameElement = screen.getByRole('treeitem', { name: /^A/ });
fireFullClick(frameElement);
fireFullClick(frameElement, { detail: 2 });
expect(sourceView()).toBeInTheDocument();
const asmViewShowButton = ensureExists(
document.querySelector('.bottom-assembly-button')
) as HTMLElement;
fireFullClick(asmViewShowButton);
expect(assemblyView()).toBeInTheDocument();
// Verify we're showing "1 of 2"
const titleTrailer = await screen.findByText(
'\u20681\u2069 of \u20682\u2069'
);
expect(titleTrailer).toBeInTheDocument();
// Find the prev and next buttons
const prevButton = ensureExists(
document.querySelector('.bottom-prev-button')
) as HTMLButtonElement;
const nextButton = ensureExists(
document.querySelector('.bottom-next-button')
) as HTMLButtonElement;
// Initially, prev should be disabled and next should be enabled
expect(prevButton).toBeDisabled();
expect(nextButton).toBeEnabled();
// Click next to go to symbol 2
fireFullClick(nextButton);
// Now we should see "2 of 2"
expect(
await screen.findByText('\u20682\u2069 of \u20682\u2069')
).toBeInTheDocument();
// Now prev should be enabled and next should be disabled
expect(prevButton).toBeEnabled();
expect(nextButton).toBeDisabled();
// Click prev to go back to symbol 1
fireFullClick(prevButton);
// We should be back to "1 of 2"
expect(
await screen.findByText('\u20681\u2069 of \u20682\u2069')
).toBeInTheDocument();
expect(prevButton).toBeDisabled();
expect(nextButton).toBeEnabled();
});
});