@theia/scm 1.73.0-next.9 → 1.74.0-next.0
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/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-result-pane.d.ts +2 -0
- package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-result-pane.d.ts.map +1 -1
- package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-result-pane.js +6 -1
- package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-result-pane.js.map +1 -1
- package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-side-pane.d.ts +2 -0
- package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-side-pane.d.ts.map +1 -1
- package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-side-pane.js +6 -1
- package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-side-pane.js.map +1 -1
- package/lib/browser/scm-amend-component.d.ts +2 -2
- package/lib/browser/scm-amend-component.d.ts.map +1 -1
- package/lib/browser/scm-repositories-widget.spec.js +83 -68
- package/lib/browser/scm-repositories-widget.spec.js.map +1 -1
- package/lib/browser/scm-tree-widget.d.ts +5 -3
- package/lib/browser/scm-tree-widget.d.ts.map +1 -1
- package/lib/browser/scm-tree-widget.js +7 -1
- package/lib/browser/scm-tree-widget.js.map +1 -1
- package/package.json +7 -7
- package/src/browser/merge-editor/view/merge-editor-panes/merge-editor-result-pane.ts +6 -3
- package/src/browser/merge-editor/view/merge-editor-panes/merge-editor-side-pane.ts +6 -3
- package/src/browser/scm-amend-component.tsx +2 -2
- package/src/browser/scm-repositories-widget.spec.ts +86 -84
- package/src/browser/scm-tree-widget.tsx +8 -5
|
@@ -14,17 +14,20 @@
|
|
|
14
14
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
|
|
17
|
-
import { enableJSDOM } from '@theia/core/lib/browser/test/jsdom';
|
|
17
|
+
import { enableJSDOM, enableReactActEnvironment } from '@theia/core/lib/browser/test/jsdom';
|
|
18
18
|
let disableJSDOM = enableJSDOM();
|
|
19
|
+
let disableReactActEnvironment = enableReactActEnvironment();
|
|
19
20
|
|
|
20
21
|
import { expect } from 'chai';
|
|
21
22
|
import * as React from '@theia/core/shared/react';
|
|
22
|
-
import
|
|
23
|
+
import { createRoot, Root } from '@theia/core/shared/react-dom/client';
|
|
24
|
+
import { MessageLoop } from '@theia/core/shared/@lumino/messaging';
|
|
23
25
|
import { Emitter } from '@theia/core/lib/common/event';
|
|
24
26
|
import { LabelParser } from '@theia/core/lib/browser/label-parser';
|
|
25
27
|
import { ScmRepositoriesWidget } from './scm-repositories-widget';
|
|
26
28
|
import { ScmRepository } from './scm-repository';
|
|
27
29
|
|
|
30
|
+
disableReactActEnvironment();
|
|
28
31
|
disableJSDOM();
|
|
29
32
|
|
|
30
33
|
interface MakeRepoOptions {
|
|
@@ -98,7 +101,10 @@ function createWidget(opts: CreateWidgetOptions): ScmRepositoriesWidget {
|
|
|
98
101
|
(widget as unknown as Record<string, unknown>).contextMenuRenderer = mockContextMenuRenderer;
|
|
99
102
|
(widget as unknown as Record<string, unknown>).scmContextKeys = mockScmContextKeys;
|
|
100
103
|
|
|
101
|
-
(
|
|
104
|
+
React.act(() => {
|
|
105
|
+
(widget as unknown as { init(): void }).init();
|
|
106
|
+
MessageLoop.flush();
|
|
107
|
+
});
|
|
102
108
|
|
|
103
109
|
return widget;
|
|
104
110
|
}
|
|
@@ -111,16 +117,20 @@ describe('ScmRepositoriesWidget', () => {
|
|
|
111
117
|
let onChangedSelectedEmitter: Emitter<ScmRepository | undefined>;
|
|
112
118
|
let executedCommands: { id: string; args: unknown[] }[];
|
|
113
119
|
let widget: ScmRepositoriesWidget;
|
|
120
|
+
let renderedRoots: Array<{ container: HTMLElement; root: Root }>;
|
|
114
121
|
|
|
115
122
|
before(() => {
|
|
116
123
|
disableJSDOM = enableJSDOM();
|
|
124
|
+
disableReactActEnvironment = enableReactActEnvironment();
|
|
117
125
|
});
|
|
118
126
|
|
|
119
127
|
after(() => {
|
|
128
|
+
disableReactActEnvironment();
|
|
120
129
|
disableJSDOM();
|
|
121
130
|
});
|
|
122
131
|
|
|
123
132
|
beforeEach(() => {
|
|
133
|
+
renderedRoots = [];
|
|
124
134
|
repositories = [];
|
|
125
135
|
selectedRepositoryRef = { value: undefined };
|
|
126
136
|
onAddEmitter = new Emitter<ScmRepository>();
|
|
@@ -138,11 +148,40 @@ describe('ScmRepositoriesWidget', () => {
|
|
|
138
148
|
});
|
|
139
149
|
|
|
140
150
|
afterEach(() => {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
151
|
+
try {
|
|
152
|
+
React.act(() => {
|
|
153
|
+
for (const { root } of renderedRoots) {
|
|
154
|
+
root.unmount();
|
|
155
|
+
}
|
|
156
|
+
widget.dispose();
|
|
157
|
+
MessageLoop.flush();
|
|
158
|
+
});
|
|
159
|
+
} finally {
|
|
160
|
+
for (const { container } of renderedRoots) {
|
|
161
|
+
container.remove();
|
|
162
|
+
}
|
|
163
|
+
renderedRoots = [];
|
|
164
|
+
onAddEmitter.dispose();
|
|
165
|
+
onRemoveEmitter.dispose();
|
|
166
|
+
onChangedSelectedEmitter.dispose();
|
|
167
|
+
}
|
|
144
168
|
});
|
|
145
169
|
|
|
170
|
+
function actAndFlush(fn: () => void): void {
|
|
171
|
+
React.act(() => {
|
|
172
|
+
fn();
|
|
173
|
+
MessageLoop.flush();
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function fireRepositoryAdded(repository: ScmRepository): void {
|
|
178
|
+
actAndFlush(() => onAddEmitter.fire(repository));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function fireRepositoryRemoved(repository: ScmRepository): void {
|
|
182
|
+
actAndFlush(() => onRemoveEmitter.fire(repository));
|
|
183
|
+
}
|
|
184
|
+
|
|
146
185
|
// --- Visibility ---
|
|
147
186
|
|
|
148
187
|
it('should not programmatically show when there are no repositories', () => {
|
|
@@ -155,7 +194,7 @@ describe('ScmRepositoriesWidget', () => {
|
|
|
155
194
|
it('should not programmatically show when there is exactly one repository', () => {
|
|
156
195
|
const repo = makeRepo({ id: 'git', label: 'Git', rootUri: '/workspace/repo1' });
|
|
157
196
|
repositories.push(repo);
|
|
158
|
-
|
|
197
|
+
fireRepositoryAdded(repo);
|
|
159
198
|
// With < 2 repos the widget does NOT call setHidden(false).
|
|
160
199
|
// It remains in its initial state (visible in standalone test, hidden in real ViewContainer).
|
|
161
200
|
expect(widget.isHidden).to.be.false;
|
|
@@ -165,7 +204,7 @@ describe('ScmRepositoriesWidget', () => {
|
|
|
165
204
|
const repo1 = makeRepo({ id: 'git', label: 'Git', rootUri: '/workspace/repo1' });
|
|
166
205
|
const repo2 = makeRepo({ id: 'git', label: 'Git', rootUri: '/workspace/repo2' });
|
|
167
206
|
repositories.push(repo1, repo2);
|
|
168
|
-
|
|
207
|
+
fireRepositoryAdded(repo2);
|
|
169
208
|
expect(widget.isHidden).to.be.false;
|
|
170
209
|
});
|
|
171
210
|
|
|
@@ -173,11 +212,11 @@ describe('ScmRepositoriesWidget', () => {
|
|
|
173
212
|
const repo1 = makeRepo({ id: 'git', label: 'Git', rootUri: '/workspace/repo1' });
|
|
174
213
|
const repo2 = makeRepo({ id: 'git', label: 'Git', rootUri: '/workspace/repo2' });
|
|
175
214
|
repositories.push(repo1, repo2);
|
|
176
|
-
|
|
215
|
+
fireRepositoryAdded(repo2);
|
|
177
216
|
expect(widget.isHidden).to.be.false;
|
|
178
217
|
|
|
179
218
|
repositories.splice(1, 1);
|
|
180
|
-
|
|
219
|
+
fireRepositoryRemoved(repo2);
|
|
181
220
|
// Widget stays visible — we never programmatically hide
|
|
182
221
|
expect(widget.isHidden).to.be.false;
|
|
183
222
|
});
|
|
@@ -188,11 +227,16 @@ describe('ScmRepositoriesWidget', () => {
|
|
|
188
227
|
|
|
189
228
|
// --- Rendering ---
|
|
190
229
|
|
|
191
|
-
function renderIntoContainer(node: React.ReactNode): HTMLElement {
|
|
230
|
+
function renderIntoContainer(node: React.ReactNode): { container: HTMLElement; root: Root } {
|
|
192
231
|
const container = document.createElement('div');
|
|
193
232
|
document.body.appendChild(container);
|
|
194
|
-
|
|
195
|
-
|
|
233
|
+
const root = createRoot(container);
|
|
234
|
+
const renderedRoot = { container, root };
|
|
235
|
+
renderedRoots.push(renderedRoot);
|
|
236
|
+
React.act(() => {
|
|
237
|
+
root.render(node);
|
|
238
|
+
});
|
|
239
|
+
return renderedRoot;
|
|
196
240
|
}
|
|
197
241
|
|
|
198
242
|
it('should render a repo icon, folder name, and status commands for each repository', () => {
|
|
@@ -202,10 +246,10 @@ describe('ScmRepositoriesWidget', () => {
|
|
|
202
246
|
});
|
|
203
247
|
const repo2 = makeRepo({ id: 'git', label: 'Git', rootUri: '/workspace/repo2' });
|
|
204
248
|
repositories.push(repo1, repo2);
|
|
205
|
-
|
|
249
|
+
fireRepositoryAdded(repo2);
|
|
206
250
|
|
|
207
251
|
const rendered = (widget as unknown as { render(): React.ReactNode }).render();
|
|
208
|
-
const container = renderIntoContainer(rendered);
|
|
252
|
+
const { container } = renderIntoContainer(rendered);
|
|
209
253
|
|
|
210
254
|
const rows = container.querySelectorAll('.theia-scm-repository-item');
|
|
211
255
|
expect(rows.length).to.equal(2);
|
|
@@ -218,9 +262,6 @@ describe('ScmRepositoriesWidget', () => {
|
|
|
218
262
|
|
|
219
263
|
const statusCmd = myrepoRow!.querySelector('.theia-scm-repository-status-command');
|
|
220
264
|
expect(statusCmd).to.exist;
|
|
221
|
-
|
|
222
|
-
ReactDOM.unmountComponentAtNode(container);
|
|
223
|
-
container.remove();
|
|
224
265
|
});
|
|
225
266
|
|
|
226
267
|
it('should apply "selected" class only to the selected repository row', () => {
|
|
@@ -228,17 +269,14 @@ describe('ScmRepositoriesWidget', () => {
|
|
|
228
269
|
const repo2 = makeRepo({ id: 'git', label: 'Git', rootUri: '/workspace/repo2' });
|
|
229
270
|
repositories.push(repo1, repo2);
|
|
230
271
|
selectedRepositoryRef.value = repo1;
|
|
231
|
-
|
|
272
|
+
fireRepositoryAdded(repo2);
|
|
232
273
|
|
|
233
274
|
const rendered = (widget as unknown as { render(): React.ReactNode }).render();
|
|
234
|
-
const container = renderIntoContainer(rendered);
|
|
275
|
+
const { container } = renderIntoContainer(rendered);
|
|
235
276
|
|
|
236
277
|
const rows = container.querySelectorAll('.theia-scm-repository-item');
|
|
237
278
|
expect(rows[0].classList.contains('selected')).to.be.true;
|
|
238
279
|
expect(rows[1].classList.contains('selected')).to.be.false;
|
|
239
|
-
|
|
240
|
-
ReactDOM.unmountComponentAtNode(container);
|
|
241
|
-
container.remove();
|
|
242
280
|
});
|
|
243
281
|
|
|
244
282
|
it('should execute the status command when clicked and stop propagation', async () => {
|
|
@@ -248,10 +286,10 @@ describe('ScmRepositoriesWidget', () => {
|
|
|
248
286
|
});
|
|
249
287
|
const repo2 = makeRepo({ id: 'git', label: 'Git', rootUri: '/workspace/repo2' });
|
|
250
288
|
repositories.push(repo1, repo2);
|
|
251
|
-
|
|
289
|
+
fireRepositoryAdded(repo2);
|
|
252
290
|
|
|
253
291
|
const rendered = (widget as unknown as { render(): React.ReactNode }).render();
|
|
254
|
-
const container = renderIntoContainer(rendered);
|
|
292
|
+
const { container } = renderIntoContainer(rendered);
|
|
255
293
|
|
|
256
294
|
const statusCmd = container.querySelector('.theia-scm-repository-status-command') as HTMLElement;
|
|
257
295
|
expect(statusCmd).to.exist;
|
|
@@ -262,9 +300,6 @@ describe('ScmRepositoriesWidget', () => {
|
|
|
262
300
|
expect(executedCommands).to.have.length(1);
|
|
263
301
|
expect(executedCommands[0].id).to.equal('git.sync');
|
|
264
302
|
expect(executedCommands[0].args).to.deep.equal(['arg1']);
|
|
265
|
-
|
|
266
|
-
ReactDOM.unmountComponentAtNode(container);
|
|
267
|
-
container.remove();
|
|
268
303
|
});
|
|
269
304
|
|
|
270
305
|
// --- Grouping: parent-based nesting ---
|
|
@@ -285,10 +320,10 @@ describe('ScmRepositoriesWidget', () => {
|
|
|
285
320
|
parentRootUri: '/projects/myrepo'
|
|
286
321
|
});
|
|
287
322
|
repositories.push(parent, wt1, wt2);
|
|
288
|
-
|
|
323
|
+
fireRepositoryAdded(wt1);
|
|
289
324
|
|
|
290
325
|
const rendered = (widget as unknown as { render(): React.ReactNode }).render();
|
|
291
|
-
const container = renderIntoContainer(rendered);
|
|
326
|
+
const { container } = renderIntoContainer(rendered);
|
|
292
327
|
|
|
293
328
|
const rows = container.querySelectorAll('.theia-scm-repository-item');
|
|
294
329
|
expect(rows.length).to.equal(3);
|
|
@@ -300,9 +335,6 @@ describe('ScmRepositoriesWidget', () => {
|
|
|
300
335
|
expect(rows[2].classList.contains('child')).to.be.true;
|
|
301
336
|
// Parent does not have .child class
|
|
302
337
|
expect(rows[0].classList.contains('child')).to.be.false;
|
|
303
|
-
|
|
304
|
-
ReactDOM.unmountComponentAtNode(container);
|
|
305
|
-
container.remove();
|
|
306
338
|
});
|
|
307
339
|
|
|
308
340
|
it('should show submodules nested under their parent repo', () => {
|
|
@@ -316,10 +348,10 @@ describe('ScmRepositoriesWidget', () => {
|
|
|
316
348
|
parentRootUri: '/projects/myrepo'
|
|
317
349
|
});
|
|
318
350
|
repositories.push(parent, sub);
|
|
319
|
-
|
|
351
|
+
fireRepositoryAdded(sub);
|
|
320
352
|
|
|
321
353
|
const rendered = (widget as unknown as { render(): React.ReactNode }).render();
|
|
322
|
-
const container = renderIntoContainer(rendered);
|
|
354
|
+
const { container } = renderIntoContainer(rendered);
|
|
323
355
|
|
|
324
356
|
const rows = container.querySelectorAll('.theia-scm-repository-item');
|
|
325
357
|
expect(rows.length).to.equal(2);
|
|
@@ -331,9 +363,6 @@ describe('ScmRepositoriesWidget', () => {
|
|
|
331
363
|
const subIcon = rows[1].querySelector('.theia-scm-repository-icon');
|
|
332
364
|
expect(subIcon).to.exist;
|
|
333
365
|
expect(subIcon!.className).to.include('codicon-file-submodule');
|
|
334
|
-
|
|
335
|
-
ReactDOM.unmountComponentAtNode(container);
|
|
336
|
-
container.remove();
|
|
337
366
|
});
|
|
338
367
|
|
|
339
368
|
it('should show independent nested repos flat without grouping', () => {
|
|
@@ -346,10 +375,10 @@ describe('ScmRepositoriesWidget', () => {
|
|
|
346
375
|
providerContextValue: 'repository'
|
|
347
376
|
});
|
|
348
377
|
repositories.push(gitRepo, nestedRepo);
|
|
349
|
-
|
|
378
|
+
fireRepositoryAdded(nestedRepo);
|
|
350
379
|
|
|
351
380
|
const rendered = (widget as unknown as { render(): React.ReactNode }).render();
|
|
352
|
-
const container = renderIntoContainer(rendered);
|
|
381
|
+
const { container } = renderIntoContainer(rendered);
|
|
353
382
|
|
|
354
383
|
const rows = container.querySelectorAll('.theia-scm-repository-item');
|
|
355
384
|
// Both repos are flat (no parent relationship set)
|
|
@@ -365,9 +394,6 @@ describe('ScmRepositoriesWidget', () => {
|
|
|
365
394
|
const icon1 = rows[1].querySelector('.theia-scm-repository-icon');
|
|
366
395
|
expect(icon0!.className).to.include('codicon-repo');
|
|
367
396
|
expect(icon1!.className).to.include('codicon-repo');
|
|
368
|
-
|
|
369
|
-
ReactDOM.unmountComponentAtNode(container);
|
|
370
|
-
container.remove();
|
|
371
397
|
});
|
|
372
398
|
|
|
373
399
|
it('should use codicon-worktree for worktrees', () => {
|
|
@@ -380,17 +406,14 @@ describe('ScmRepositoriesWidget', () => {
|
|
|
380
406
|
parentRootUri: '/projects/myrepo'
|
|
381
407
|
});
|
|
382
408
|
repositories.push(parent, wt);
|
|
383
|
-
|
|
409
|
+
fireRepositoryAdded(wt);
|
|
384
410
|
|
|
385
411
|
const rendered = (widget as unknown as { render(): React.ReactNode }).render();
|
|
386
|
-
const container = renderIntoContainer(rendered);
|
|
412
|
+
const { container } = renderIntoContainer(rendered);
|
|
387
413
|
|
|
388
414
|
const rows = container.querySelectorAll('.theia-scm-repository-item');
|
|
389
415
|
const wtIcon = rows[1].querySelector('.theia-scm-repository-icon');
|
|
390
416
|
expect(wtIcon!.className).to.include('codicon-worktree');
|
|
391
|
-
|
|
392
|
-
ReactDOM.unmountComponentAtNode(container);
|
|
393
|
-
container.remove();
|
|
394
417
|
});
|
|
395
418
|
|
|
396
419
|
it('should handle mixed scenario: parent + worktree + submodule + independent repo', () => {
|
|
@@ -413,10 +436,10 @@ describe('ScmRepositoriesWidget', () => {
|
|
|
413
436
|
providerContextValue: 'repository'
|
|
414
437
|
});
|
|
415
438
|
repositories.push(parent, wt, sub, independent);
|
|
416
|
-
|
|
439
|
+
fireRepositoryAdded(independent);
|
|
417
440
|
|
|
418
441
|
const rendered = (widget as unknown as { render(): React.ReactNode }).render();
|
|
419
|
-
const container = renderIntoContainer(rendered);
|
|
442
|
+
const { container } = renderIntoContainer(rendered);
|
|
420
443
|
|
|
421
444
|
const rows = container.querySelectorAll('.theia-scm-repository-item');
|
|
422
445
|
// parent (root with toggle) + wt (child) + sub (child) + independent (root, no toggle)
|
|
@@ -433,9 +456,6 @@ describe('ScmRepositoriesWidget', () => {
|
|
|
433
456
|
// Fourth: independent, flat, no collapse toggle
|
|
434
457
|
expect(rows[3].classList.contains('child')).to.be.false;
|
|
435
458
|
expect(rows[3].querySelector('.theia-scm-repository-collapse-toggle')).to.not.exist;
|
|
436
|
-
|
|
437
|
-
ReactDOM.unmountComponentAtNode(container);
|
|
438
|
-
container.remove();
|
|
439
459
|
});
|
|
440
460
|
|
|
441
461
|
it('should hide children when parent is collapsed', () => {
|
|
@@ -448,48 +468,42 @@ describe('ScmRepositoriesWidget', () => {
|
|
|
448
468
|
parentRootUri: '/projects/myrepo'
|
|
449
469
|
});
|
|
450
470
|
repositories.push(parent, child);
|
|
451
|
-
|
|
471
|
+
fireRepositoryAdded(child);
|
|
452
472
|
|
|
453
473
|
// Collapse the parent
|
|
454
|
-
(widget as unknown as { toggleCollapse(uri: string): void }).toggleCollapse(parent.provider.rootUri);
|
|
474
|
+
actAndFlush(() => (widget as unknown as { toggleCollapse(uri: string): void }).toggleCollapse(parent.provider.rootUri));
|
|
455
475
|
|
|
456
476
|
const rendered = (widget as unknown as { render(): React.ReactNode }).render();
|
|
457
|
-
const container = renderIntoContainer(rendered);
|
|
477
|
+
const { container } = renderIntoContainer(rendered);
|
|
458
478
|
|
|
459
479
|
const rows = container.querySelectorAll('.theia-scm-repository-item');
|
|
460
480
|
expect(rows.length).to.equal(1);
|
|
461
481
|
const toggle = rows[0].querySelector('.theia-scm-repository-collapse-toggle');
|
|
462
482
|
expect(toggle).to.exist;
|
|
463
483
|
expect(toggle!.className).to.include('codicon-chevron-right');
|
|
464
|
-
|
|
465
|
-
ReactDOM.unmountComponentAtNode(container);
|
|
466
|
-
container.remove();
|
|
467
484
|
});
|
|
468
485
|
|
|
469
486
|
it('should render a ... more-button on each repository row', () => {
|
|
470
487
|
const repo1 = makeRepo({ id: 'git', label: 'Git', rootUri: '/workspace/repo1' });
|
|
471
488
|
const repo2 = makeRepo({ id: 'git', label: 'Git', rootUri: '/workspace/repo2' });
|
|
472
489
|
repositories.push(repo1, repo2);
|
|
473
|
-
|
|
490
|
+
fireRepositoryAdded(repo2);
|
|
474
491
|
|
|
475
492
|
const rendered = (widget as unknown as { render(): React.ReactNode }).render();
|
|
476
|
-
const container = renderIntoContainer(rendered);
|
|
493
|
+
const { container } = renderIntoContainer(rendered);
|
|
477
494
|
|
|
478
495
|
const moreButtons = container.querySelectorAll('.theia-scm-repository-more-button');
|
|
479
496
|
expect(moreButtons.length).to.equal(2);
|
|
480
|
-
|
|
481
|
-
ReactDOM.unmountComponentAtNode(container);
|
|
482
|
-
container.remove();
|
|
483
497
|
});
|
|
484
498
|
|
|
485
499
|
it('should create separate flat entries for repos with different provider ids and no parent', () => {
|
|
486
500
|
const gitRepo = makeRepo({ id: 'git', label: 'Git', rootUri: '/workspace/myrepo' });
|
|
487
501
|
const svnRepo = makeRepo({ id: 'svn', label: 'SVN', rootUri: '/workspace/other' });
|
|
488
502
|
repositories.push(gitRepo, svnRepo);
|
|
489
|
-
|
|
503
|
+
fireRepositoryAdded(svnRepo);
|
|
490
504
|
|
|
491
505
|
const rendered = (widget as unknown as { render(): React.ReactNode }).render();
|
|
492
|
-
const container = renderIntoContainer(rendered);
|
|
506
|
+
const { container } = renderIntoContainer(rendered);
|
|
493
507
|
|
|
494
508
|
const rows = container.querySelectorAll('.theia-scm-repository-item');
|
|
495
509
|
expect(rows.length).to.equal(2);
|
|
@@ -497,9 +511,6 @@ describe('ScmRepositoriesWidget', () => {
|
|
|
497
511
|
expect(rows[1].classList.contains('child')).to.be.false;
|
|
498
512
|
expect(rows[0].querySelector('.theia-scm-repository-collapse-toggle')).to.not.exist;
|
|
499
513
|
expect(rows[1].querySelector('.theia-scm-repository-collapse-toggle')).to.not.exist;
|
|
500
|
-
|
|
501
|
-
ReactDOM.unmountComponentAtNode(container);
|
|
502
|
-
container.remove();
|
|
503
514
|
});
|
|
504
515
|
|
|
505
516
|
// --- Duplicate name disambiguation ---
|
|
@@ -509,10 +520,10 @@ describe('ScmRepositoriesWidget', () => {
|
|
|
509
520
|
const repo1 = makeRepo({ id: 'git', label: 'Git', rootUri: '/workspace/foo' });
|
|
510
521
|
const repo2 = makeRepo({ id: 'git', label: 'Git', rootUri: '/workspace/bar/foo' });
|
|
511
522
|
repositories.push(repo1, repo2);
|
|
512
|
-
|
|
523
|
+
fireRepositoryAdded(repo2);
|
|
513
524
|
|
|
514
525
|
const rendered = (widget as unknown as { render(): React.ReactNode }).render();
|
|
515
|
-
const container = renderIntoContainer(rendered);
|
|
526
|
+
const { container } = renderIntoContainer(rendered);
|
|
516
527
|
|
|
517
528
|
const rows = container.querySelectorAll('.theia-scm-repository-item');
|
|
518
529
|
expect(rows.length).to.equal(2);
|
|
@@ -525,19 +536,16 @@ describe('ScmRepositoriesWidget', () => {
|
|
|
525
536
|
// Repo at workspace root shows "/", nested repo shows relative path
|
|
526
537
|
expect(desc0!.textContent).to.equal('/');
|
|
527
538
|
expect(desc1!.textContent).to.equal('bar');
|
|
528
|
-
|
|
529
|
-
ReactDOM.unmountComponentAtNode(container);
|
|
530
|
-
container.remove();
|
|
531
539
|
});
|
|
532
540
|
|
|
533
541
|
it('should show relative descriptions when duplicate names are in sibling directories', () => {
|
|
534
542
|
const repo1 = makeRepo({ id: 'git', label: 'Git', rootUri: '/workspace/projects/lib' });
|
|
535
543
|
const repo2 = makeRepo({ id: 'git', label: 'Git', rootUri: '/workspace/vendor/lib' });
|
|
536
544
|
repositories.push(repo1, repo2);
|
|
537
|
-
|
|
545
|
+
fireRepositoryAdded(repo2);
|
|
538
546
|
|
|
539
547
|
const rendered = (widget as unknown as { render(): React.ReactNode }).render();
|
|
540
|
-
const container = renderIntoContainer(rendered);
|
|
548
|
+
const { container } = renderIntoContainer(rendered);
|
|
541
549
|
|
|
542
550
|
const rows = container.querySelectorAll('.theia-scm-repository-item');
|
|
543
551
|
expect(rows.length).to.equal(2);
|
|
@@ -549,19 +557,16 @@ describe('ScmRepositoriesWidget', () => {
|
|
|
549
557
|
|
|
550
558
|
expect(desc0!.textContent).to.equal('projects');
|
|
551
559
|
expect(desc1!.textContent).to.equal('vendor');
|
|
552
|
-
|
|
553
|
-
ReactDOM.unmountComponentAtNode(container);
|
|
554
|
-
container.remove();
|
|
555
560
|
});
|
|
556
561
|
|
|
557
562
|
it('should not show a description path when all repos have unique folder names', () => {
|
|
558
563
|
const repo1 = makeRepo({ id: 'git', label: 'Git', rootUri: '/workspace/alpha' });
|
|
559
564
|
const repo2 = makeRepo({ id: 'git', label: 'Git', rootUri: '/workspace/beta' });
|
|
560
565
|
repositories.push(repo1, repo2);
|
|
561
|
-
|
|
566
|
+
fireRepositoryAdded(repo2);
|
|
562
567
|
|
|
563
568
|
const rendered = (widget as unknown as { render(): React.ReactNode }).render();
|
|
564
|
-
const container = renderIntoContainer(rendered);
|
|
569
|
+
const { container } = renderIntoContainer(rendered);
|
|
565
570
|
|
|
566
571
|
const rows = container.querySelectorAll('.theia-scm-repository-item');
|
|
567
572
|
expect(rows.length).to.equal(2);
|
|
@@ -569,8 +574,5 @@ describe('ScmRepositoriesWidget', () => {
|
|
|
569
574
|
// No description elements since names are unique
|
|
570
575
|
expect(rows[0].querySelector('.theia-scm-repository-description')).to.not.exist;
|
|
571
576
|
expect(rows[1].querySelector('.theia-scm-repository-description')).to.not.exist;
|
|
572
|
-
|
|
573
|
-
ReactDOM.unmountComponentAtNode(container);
|
|
574
|
-
container.remove();
|
|
575
577
|
});
|
|
576
578
|
});
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
/* eslint-disable no-null/no-null, @typescript-eslint/no-explicit-any */
|
|
18
18
|
|
|
19
19
|
import * as React from '@theia/core/shared/react';
|
|
20
|
-
import { injectable, inject, postConstruct } from '@theia/core/shared/inversify';
|
|
20
|
+
import { injectable, inject, postConstruct, named } from '@theia/core/shared/inversify';
|
|
21
21
|
import URI from '@theia/core/lib/common/uri';
|
|
22
22
|
import { isOSX } from '@theia/core/lib/common/os';
|
|
23
23
|
import { DisposableCollection, Disposable } from '@theia/core/lib/common/disposable';
|
|
@@ -36,6 +36,7 @@ import { ScmService } from './scm-service';
|
|
|
36
36
|
import { FileStat } from '@theia/filesystem/lib/common/files';
|
|
37
37
|
import { ThemeService } from '@theia/core/lib/browser/theming';
|
|
38
38
|
import { CorePreferences } from '@theia/core/lib/common';
|
|
39
|
+
import { ILogger } from '@theia/core';
|
|
39
40
|
|
|
40
41
|
@injectable()
|
|
41
42
|
export class ScmTreeWidget extends TreeViewWelcomeWidget {
|
|
@@ -59,6 +60,8 @@ export class ScmTreeWidget extends TreeViewWelcomeWidget {
|
|
|
59
60
|
@inject(DecorationsService) protected readonly decorationsService: DecorationsService;
|
|
60
61
|
@inject(ColorRegistry) protected readonly colors: ColorRegistry;
|
|
61
62
|
@inject(ThemeService) protected readonly themeService: ThemeService;
|
|
63
|
+
@inject(ILogger) @named('scm:ScmTreeWidget')
|
|
64
|
+
protected readonly logger: ILogger;
|
|
62
65
|
@inject(ScmService) protected readonly scmService: ScmService;
|
|
63
66
|
|
|
64
67
|
// TODO: Make TreeWidget generic to better type those fields.
|
|
@@ -426,7 +429,7 @@ export class ScmTreeWidget extends TreeViewWelcomeWidget {
|
|
|
426
429
|
try {
|
|
427
430
|
await resource.open();
|
|
428
431
|
} catch (e) {
|
|
429
|
-
|
|
432
|
+
this.logger.error('Failed to open a SCM resource', e);
|
|
430
433
|
return undefined;
|
|
431
434
|
}
|
|
432
435
|
|
|
@@ -561,7 +564,7 @@ export namespace ScmElement {
|
|
|
561
564
|
|
|
562
565
|
export class ScmResourceComponent extends ScmElement<ScmResourceComponent.Props> {
|
|
563
566
|
|
|
564
|
-
override render(): JSX.Element | undefined {
|
|
567
|
+
override render(): React.JSX.Element | undefined {
|
|
565
568
|
const { hover } = this.state;
|
|
566
569
|
const { model, treeNode, colors, parentPath, sourceUri, decoration, labelProvider, menus, contextKeys, caption, isLightTheme } = this.props;
|
|
567
570
|
const resourceUri = new URI(sourceUri);
|
|
@@ -682,7 +685,7 @@ export namespace ScmResourceComponent {
|
|
|
682
685
|
|
|
683
686
|
export class ScmResourceGroupElement extends ScmElement<ScmResourceGroupComponent.Props> {
|
|
684
687
|
|
|
685
|
-
override render(): JSX.Element {
|
|
688
|
+
override render(): React.JSX.Element {
|
|
686
689
|
const { hover } = this.state;
|
|
687
690
|
const { model, treeNode, menus, contextKeys, caption } = this.props;
|
|
688
691
|
return <div className={`theia-header scm-theia-header ${TREE_NODE_SEGMENT_GROW_CLASS}`}
|
|
@@ -732,7 +735,7 @@ export namespace ScmResourceGroupComponent {
|
|
|
732
735
|
|
|
733
736
|
export class ScmResourceFolderElement extends ScmElement<ScmResourceFolderElement.Props> {
|
|
734
737
|
|
|
735
|
-
override render(): JSX.Element {
|
|
738
|
+
override render(): React.JSX.Element {
|
|
736
739
|
const { hover } = this.state;
|
|
737
740
|
const { model, treeNode, sourceUri, labelProvider, menus, contextKeys, caption } = this.props;
|
|
738
741
|
const sourceFileStat = FileStat.dir(sourceUri);
|