@theia/scm 1.73.0-next.7 → 1.73.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.
Files changed (22) hide show
  1. package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-result-pane.d.ts +2 -0
  2. package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-result-pane.d.ts.map +1 -1
  3. package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-result-pane.js +6 -1
  4. package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-result-pane.js.map +1 -1
  5. package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-side-pane.d.ts +2 -0
  6. package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-side-pane.d.ts.map +1 -1
  7. package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-side-pane.js +6 -1
  8. package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-side-pane.js.map +1 -1
  9. package/lib/browser/scm-amend-component.d.ts +2 -2
  10. package/lib/browser/scm-amend-component.d.ts.map +1 -1
  11. package/lib/browser/scm-repositories-widget.spec.js +83 -68
  12. package/lib/browser/scm-repositories-widget.spec.js.map +1 -1
  13. package/lib/browser/scm-tree-widget.d.ts +5 -3
  14. package/lib/browser/scm-tree-widget.d.ts.map +1 -1
  15. package/lib/browser/scm-tree-widget.js +7 -1
  16. package/lib/browser/scm-tree-widget.js.map +1 -1
  17. package/package.json +7 -7
  18. package/src/browser/merge-editor/view/merge-editor-panes/merge-editor-result-pane.ts +6 -3
  19. package/src/browser/merge-editor/view/merge-editor-panes/merge-editor-side-pane.ts +6 -3
  20. package/src/browser/scm-amend-component.tsx +2 -2
  21. package/src/browser/scm-repositories-widget.spec.ts +86 -84
  22. 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 * as ReactDOM from '@theia/core/shared/react-dom';
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
- (widget as unknown as { init(): void }).init();
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
- onAddEmitter.dispose();
142
- onRemoveEmitter.dispose();
143
- onChangedSelectedEmitter.dispose();
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
- onAddEmitter.fire(repo);
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
- onAddEmitter.fire(repo2);
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
- onAddEmitter.fire(repo2);
215
+ fireRepositoryAdded(repo2);
177
216
  expect(widget.isHidden).to.be.false;
178
217
 
179
218
  repositories.splice(1, 1);
180
- onRemoveEmitter.fire(repo2);
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
- ReactDOM.render(node as React.ReactElement, container);
195
- return container;
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
- onAddEmitter.fire(repo2);
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
- onAddEmitter.fire(repo2);
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
- onAddEmitter.fire(repo2);
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
- onAddEmitter.fire(wt1);
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
- onAddEmitter.fire(sub);
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
- onAddEmitter.fire(nestedRepo);
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
- onAddEmitter.fire(wt);
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
- onAddEmitter.fire(independent);
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
- onAddEmitter.fire(child);
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
- onAddEmitter.fire(repo2);
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
- onAddEmitter.fire(svnRepo);
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
- onAddEmitter.fire(repo2);
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
- onAddEmitter.fire(repo2);
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
- onAddEmitter.fire(repo2);
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
- console.error('Failed to open a SCM resource', e);
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);