datajunction-ui 0.0.55 → 0.0.57
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/package.json +1 -1
- package/src/app/components/NamespaceHeader.jsx +423 -12
- package/src/app/components/NodeListActions.jsx +10 -7
- package/src/app/components/__tests__/GitModals.test.jsx +1293 -0
- package/src/app/components/__tests__/NamespaceHeader.test.jsx +905 -16
- package/src/app/components/__tests__/NodeListActions.test.jsx +5 -3
- package/src/app/components/__tests__/__snapshots__/NamespaceHeader.test.jsx.snap +41 -0
- package/src/app/components/git/CreateBranchModal.jsx +229 -0
- package/src/app/components/git/CreatePRModal.jsx +270 -0
- package/src/app/components/git/DeleteBranchModal.jsx +173 -0
- package/src/app/components/git/GitSettingsModal.jsx +375 -0
- package/src/app/components/git/SyncToGitModal.jsx +219 -0
- package/src/app/components/git/__tests__/GitSettingsModal.test.jsx +301 -0
- package/src/app/components/git/index.js +5 -0
- package/src/app/icons/DeleteIcon.jsx +3 -3
- package/src/app/icons/EditIcon.jsx +3 -3
- package/src/app/icons/EyeIcon.jsx +3 -4
- package/src/app/icons/JupyterExportIcon.jsx +3 -7
- package/src/app/icons/PythonIcon.jsx +3 -3
- package/src/app/pages/AddEditNodePage/index.jsx +8 -5
- package/src/app/pages/NamespacePage/index.jsx +34 -21
- package/src/app/pages/NodePage/ClientCodePopover.jsx +3 -7
- package/src/app/pages/NodePage/NodeInfoTab.jsx +10 -3
- package/src/app/pages/NodePage/NotebookDownload.jsx +4 -10
- package/src/app/pages/NodePage/WatchNodeButton.jsx +7 -12
- package/src/app/pages/NodePage/index.jsx +42 -13
- package/src/app/services/DJService.js +218 -1
- package/src/styles/index.css +3 -0
- package/src/styles/node-creation.scss +22 -0
- package/src/styles/settings.css +1 -1
|
@@ -42,8 +42,8 @@ describe('<NamespaceHeader />', () => {
|
|
|
42
42
|
);
|
|
43
43
|
});
|
|
44
44
|
|
|
45
|
-
// Should render Git
|
|
46
|
-
expect(screen.getByText(/Git
|
|
45
|
+
// Should render Deployed from Git badge for git source
|
|
46
|
+
expect(screen.getByText(/Deployed from Git/)).toBeInTheDocument();
|
|
47
47
|
});
|
|
48
48
|
|
|
49
49
|
it('should render git source badge when source type is git without branch', async () => {
|
|
@@ -73,8 +73,8 @@ describe('<NamespaceHeader />', () => {
|
|
|
73
73
|
);
|
|
74
74
|
});
|
|
75
75
|
|
|
76
|
-
// Should render Git
|
|
77
|
-
expect(screen.getByText(/Git
|
|
76
|
+
// Should render Deployed from Git badge for git source even without branch
|
|
77
|
+
expect(screen.getByText(/Deployed from Git/)).toBeInTheDocument();
|
|
78
78
|
});
|
|
79
79
|
|
|
80
80
|
it('should render local source badge when source type is local', async () => {
|
|
@@ -131,7 +131,7 @@ describe('<NamespaceHeader />', () => {
|
|
|
131
131
|
});
|
|
132
132
|
|
|
133
133
|
// Should not render any source badge
|
|
134
|
-
expect(screen.queryByText(/Git
|
|
134
|
+
expect(screen.queryByText(/Deployed from Git/)).not.toBeInTheDocument();
|
|
135
135
|
expect(screen.queryByText(/Local Deploy/)).not.toBeInTheDocument();
|
|
136
136
|
});
|
|
137
137
|
|
|
@@ -158,7 +158,7 @@ describe('<NamespaceHeader />', () => {
|
|
|
158
158
|
// Should still render breadcrumb without badge
|
|
159
159
|
expect(screen.getByText('test')).toBeInTheDocument();
|
|
160
160
|
expect(screen.getByText('namespace')).toBeInTheDocument();
|
|
161
|
-
expect(screen.queryByText(/Git
|
|
161
|
+
expect(screen.queryByText(/Deployed from Git/)).not.toBeInTheDocument();
|
|
162
162
|
});
|
|
163
163
|
|
|
164
164
|
it('should open dropdown when clicking the git managed button', async () => {
|
|
@@ -195,11 +195,11 @@ describe('<NamespaceHeader />', () => {
|
|
|
195
195
|
);
|
|
196
196
|
|
|
197
197
|
await waitFor(() => {
|
|
198
|
-
expect(screen.getByText(/Git
|
|
198
|
+
expect(screen.getByText(/Deployed from Git/)).toBeInTheDocument();
|
|
199
199
|
});
|
|
200
200
|
|
|
201
201
|
// Click the dropdown button
|
|
202
|
-
fireEvent.click(screen.getByText(/Git
|
|
202
|
+
fireEvent.click(screen.getByText(/Deployed from Git/));
|
|
203
203
|
|
|
204
204
|
// Should show repository link in dropdown
|
|
205
205
|
await waitFor(() => {
|
|
@@ -297,10 +297,10 @@ describe('<NamespaceHeader />', () => {
|
|
|
297
297
|
);
|
|
298
298
|
|
|
299
299
|
await waitFor(() => {
|
|
300
|
-
expect(screen.getByText(/Git
|
|
300
|
+
expect(screen.getByText(/Deployed from Git/)).toBeInTheDocument();
|
|
301
301
|
});
|
|
302
302
|
|
|
303
|
-
fireEvent.click(screen.getByText(/Git
|
|
303
|
+
fireEvent.click(screen.getByText(/Deployed from Git/));
|
|
304
304
|
|
|
305
305
|
// Should show branch names in deployment list
|
|
306
306
|
await waitFor(() => {
|
|
@@ -375,11 +375,11 @@ describe('<NamespaceHeader />', () => {
|
|
|
375
375
|
);
|
|
376
376
|
|
|
377
377
|
await waitFor(() => {
|
|
378
|
-
expect(screen.getByText(/Git
|
|
378
|
+
expect(screen.getByText(/Deployed from Git/)).toBeInTheDocument();
|
|
379
379
|
});
|
|
380
380
|
|
|
381
381
|
// Open dropdown
|
|
382
|
-
fireEvent.click(screen.getByText(/Git
|
|
382
|
+
fireEvent.click(screen.getByText(/Deployed from Git/));
|
|
383
383
|
|
|
384
384
|
await waitFor(() => {
|
|
385
385
|
expect(screen.getByText(/github.com\/test\/repo/)).toBeInTheDocument();
|
|
@@ -418,14 +418,14 @@ describe('<NamespaceHeader />', () => {
|
|
|
418
418
|
);
|
|
419
419
|
|
|
420
420
|
await waitFor(() => {
|
|
421
|
-
expect(screen.getByText(/Git
|
|
421
|
+
expect(screen.getByText(/Deployed from Git/)).toBeInTheDocument();
|
|
422
422
|
});
|
|
423
423
|
|
|
424
424
|
// Initially shows down arrow
|
|
425
425
|
expect(screen.getByText('▼')).toBeInTheDocument();
|
|
426
426
|
|
|
427
427
|
// Click to open
|
|
428
|
-
fireEvent.click(screen.getByText(/Git
|
|
428
|
+
fireEvent.click(screen.getByText(/Deployed from Git/));
|
|
429
429
|
|
|
430
430
|
// Should show up arrow when open
|
|
431
431
|
await waitFor(() => {
|
|
@@ -455,10 +455,10 @@ describe('<NamespaceHeader />', () => {
|
|
|
455
455
|
);
|
|
456
456
|
|
|
457
457
|
await waitFor(() => {
|
|
458
|
-
expect(screen.getByText(/Git
|
|
458
|
+
expect(screen.getByText(/Deployed from Git/)).toBeInTheDocument();
|
|
459
459
|
});
|
|
460
460
|
|
|
461
|
-
fireEvent.click(screen.getByText(/Git
|
|
461
|
+
fireEvent.click(screen.getByText(/Deployed from Git/));
|
|
462
462
|
|
|
463
463
|
await waitFor(() => {
|
|
464
464
|
// Find link by its text content (repository URL)
|
|
@@ -508,4 +508,893 @@ describe('<NamespaceHeader />', () => {
|
|
|
508
508
|
expect(screen.getByText(/Local\/adhoc deployments/)).toBeInTheDocument();
|
|
509
509
|
});
|
|
510
510
|
});
|
|
511
|
+
|
|
512
|
+
it('should show Git Settings button and open modal', async () => {
|
|
513
|
+
const mockDjClient = {
|
|
514
|
+
namespaceSources: jest.fn().mockResolvedValue({
|
|
515
|
+
total_deployments: 0,
|
|
516
|
+
primary_source: null,
|
|
517
|
+
}),
|
|
518
|
+
listDeployments: jest.fn().mockResolvedValue([]),
|
|
519
|
+
getNamespaceGitConfig: jest.fn().mockResolvedValue(null),
|
|
520
|
+
};
|
|
521
|
+
|
|
522
|
+
render(
|
|
523
|
+
<MemoryRouter>
|
|
524
|
+
<DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
|
|
525
|
+
<NamespaceHeader namespace="test.namespace" />
|
|
526
|
+
</DJClientContext.Provider>
|
|
527
|
+
</MemoryRouter>,
|
|
528
|
+
);
|
|
529
|
+
|
|
530
|
+
await waitFor(() => {
|
|
531
|
+
expect(screen.getByText('Git Settings')).toBeInTheDocument();
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
fireEvent.click(screen.getByText('Git Settings'));
|
|
535
|
+
|
|
536
|
+
await waitFor(() => {
|
|
537
|
+
expect(screen.getByText('Git Configuration')).toBeInTheDocument();
|
|
538
|
+
});
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
it('should show git action buttons when git is configured', async () => {
|
|
542
|
+
const mockDjClient = {
|
|
543
|
+
namespaceSources: jest.fn().mockResolvedValue({
|
|
544
|
+
total_deployments: 1,
|
|
545
|
+
primary_source: {
|
|
546
|
+
type: 'git',
|
|
547
|
+
repository: 'test/repo',
|
|
548
|
+
branch: 'main',
|
|
549
|
+
},
|
|
550
|
+
}),
|
|
551
|
+
listDeployments: jest.fn().mockResolvedValue([]),
|
|
552
|
+
getNamespaceGitConfig: jest.fn().mockResolvedValue({
|
|
553
|
+
github_repo_path: 'test/repo',
|
|
554
|
+
git_branch: 'main',
|
|
555
|
+
git_path: 'nodes/',
|
|
556
|
+
git_only: false,
|
|
557
|
+
}),
|
|
558
|
+
};
|
|
559
|
+
|
|
560
|
+
render(
|
|
561
|
+
<MemoryRouter>
|
|
562
|
+
<DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
|
|
563
|
+
<NamespaceHeader namespace="test.namespace" />
|
|
564
|
+
</DJClientContext.Provider>
|
|
565
|
+
</MemoryRouter>,
|
|
566
|
+
);
|
|
567
|
+
|
|
568
|
+
// For non-branch namespaces, button is labeled "New Branch"
|
|
569
|
+
await waitFor(() => {
|
|
570
|
+
expect(screen.getByText('New Branch')).toBeInTheDocument();
|
|
571
|
+
});
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
it('should show Create PR and Delete Branch for branch namespaces', async () => {
|
|
575
|
+
const mockDjClient = {
|
|
576
|
+
namespaceSources: jest.fn().mockResolvedValue({
|
|
577
|
+
total_deployments: 1,
|
|
578
|
+
primary_source: {
|
|
579
|
+
type: 'git',
|
|
580
|
+
repository: 'test/repo',
|
|
581
|
+
branch: 'feature',
|
|
582
|
+
},
|
|
583
|
+
}),
|
|
584
|
+
listDeployments: jest.fn().mockResolvedValue([]),
|
|
585
|
+
getNamespaceGitConfig: jest
|
|
586
|
+
.fn()
|
|
587
|
+
.mockResolvedValueOnce({
|
|
588
|
+
github_repo_path: 'test/repo',
|
|
589
|
+
git_branch: 'feature',
|
|
590
|
+
git_path: 'nodes/',
|
|
591
|
+
git_only: false,
|
|
592
|
+
parent_namespace: 'test.main',
|
|
593
|
+
})
|
|
594
|
+
.mockResolvedValueOnce({
|
|
595
|
+
github_repo_path: 'test/repo',
|
|
596
|
+
git_branch: 'main',
|
|
597
|
+
git_path: 'nodes/',
|
|
598
|
+
}),
|
|
599
|
+
getPullRequest: jest.fn().mockResolvedValue(null),
|
|
600
|
+
};
|
|
601
|
+
|
|
602
|
+
render(
|
|
603
|
+
<MemoryRouter>
|
|
604
|
+
<DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
|
|
605
|
+
<NamespaceHeader namespace="test.feature" />
|
|
606
|
+
</DJClientContext.Provider>
|
|
607
|
+
</MemoryRouter>,
|
|
608
|
+
);
|
|
609
|
+
|
|
610
|
+
await waitFor(() => {
|
|
611
|
+
expect(screen.getByText('Create PR')).toBeInTheDocument();
|
|
612
|
+
});
|
|
613
|
+
// Delete Branch button only has an icon with title attribute
|
|
614
|
+
expect(screen.getByTitle('Delete Branch')).toBeInTheDocument();
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
it('should open Create Branch modal when button is clicked', async () => {
|
|
618
|
+
const mockDjClient = {
|
|
619
|
+
namespaceSources: jest.fn().mockResolvedValue({
|
|
620
|
+
total_deployments: 1,
|
|
621
|
+
primary_source: {
|
|
622
|
+
type: 'git',
|
|
623
|
+
repository: 'test/repo',
|
|
624
|
+
branch: 'main',
|
|
625
|
+
},
|
|
626
|
+
}),
|
|
627
|
+
listDeployments: jest.fn().mockResolvedValue([]),
|
|
628
|
+
getNamespaceGitConfig: jest.fn().mockResolvedValue({
|
|
629
|
+
github_repo_path: 'test/repo',
|
|
630
|
+
git_branch: 'main',
|
|
631
|
+
git_path: 'nodes/',
|
|
632
|
+
git_only: false,
|
|
633
|
+
}),
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
render(
|
|
637
|
+
<MemoryRouter>
|
|
638
|
+
<DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
|
|
639
|
+
<NamespaceHeader namespace="test.namespace" />
|
|
640
|
+
</DJClientContext.Provider>
|
|
641
|
+
</MemoryRouter>,
|
|
642
|
+
);
|
|
643
|
+
|
|
644
|
+
await waitFor(() => {
|
|
645
|
+
expect(screen.getByText('New Branch')).toBeInTheDocument();
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
fireEvent.click(screen.getByText('New Branch'));
|
|
649
|
+
|
|
650
|
+
await waitFor(() => {
|
|
651
|
+
expect(screen.getByLabelText('Branch Name')).toBeInTheDocument();
|
|
652
|
+
});
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
it('should open Sync to Git modal when button is clicked', async () => {
|
|
656
|
+
// Sync to Git only shows for branch namespaces
|
|
657
|
+
const mockDjClient = {
|
|
658
|
+
namespaceSources: jest.fn().mockResolvedValue({
|
|
659
|
+
total_deployments: 1,
|
|
660
|
+
primary_source: {
|
|
661
|
+
type: 'git',
|
|
662
|
+
repository: 'test/repo',
|
|
663
|
+
branch: 'feature',
|
|
664
|
+
},
|
|
665
|
+
}),
|
|
666
|
+
listDeployments: jest.fn().mockResolvedValue([]),
|
|
667
|
+
getNamespaceGitConfig: jest
|
|
668
|
+
.fn()
|
|
669
|
+
.mockResolvedValueOnce({
|
|
670
|
+
github_repo_path: 'test/repo',
|
|
671
|
+
git_branch: 'feature',
|
|
672
|
+
git_path: 'nodes/',
|
|
673
|
+
git_only: false,
|
|
674
|
+
parent_namespace: 'test.main',
|
|
675
|
+
})
|
|
676
|
+
.mockResolvedValueOnce({
|
|
677
|
+
github_repo_path: 'test/repo',
|
|
678
|
+
git_branch: 'main',
|
|
679
|
+
git_path: 'nodes/',
|
|
680
|
+
}),
|
|
681
|
+
getPullRequest: jest.fn().mockResolvedValue(null),
|
|
682
|
+
};
|
|
683
|
+
|
|
684
|
+
render(
|
|
685
|
+
<MemoryRouter>
|
|
686
|
+
<DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
|
|
687
|
+
<NamespaceHeader namespace="test.feature" />
|
|
688
|
+
</DJClientContext.Provider>
|
|
689
|
+
</MemoryRouter>,
|
|
690
|
+
);
|
|
691
|
+
|
|
692
|
+
await waitFor(() => {
|
|
693
|
+
expect(screen.getByText('Sync to Git')).toBeInTheDocument();
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
fireEvent.click(screen.getByText('Sync to Git'));
|
|
697
|
+
|
|
698
|
+
await waitFor(() => {
|
|
699
|
+
expect(screen.getByText(/Sync all nodes in/)).toBeInTheDocument();
|
|
700
|
+
});
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
it('should call updateNamespaceGitConfig when saving git settings', async () => {
|
|
704
|
+
const mockDjClient = {
|
|
705
|
+
namespaceSources: jest.fn().mockResolvedValue({
|
|
706
|
+
total_deployments: 0,
|
|
707
|
+
primary_source: null,
|
|
708
|
+
}),
|
|
709
|
+
listDeployments: jest.fn().mockResolvedValue([]),
|
|
710
|
+
getNamespaceGitConfig: jest.fn().mockResolvedValue(null),
|
|
711
|
+
updateNamespaceGitConfig: jest.fn().mockResolvedValue({
|
|
712
|
+
github_repo_path: 'myorg/repo',
|
|
713
|
+
git_branch: 'main',
|
|
714
|
+
git_path: 'nodes/',
|
|
715
|
+
git_only: true,
|
|
716
|
+
}),
|
|
717
|
+
};
|
|
718
|
+
|
|
719
|
+
render(
|
|
720
|
+
<MemoryRouter>
|
|
721
|
+
<DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
|
|
722
|
+
<NamespaceHeader namespace="test.namespace" />
|
|
723
|
+
</DJClientContext.Provider>
|
|
724
|
+
</MemoryRouter>,
|
|
725
|
+
);
|
|
726
|
+
|
|
727
|
+
await waitFor(() => {
|
|
728
|
+
expect(screen.getByText('Git Settings')).toBeInTheDocument();
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
fireEvent.click(screen.getByText('Git Settings'));
|
|
732
|
+
|
|
733
|
+
await waitFor(() => {
|
|
734
|
+
expect(screen.getByLabelText('Repository')).toBeInTheDocument();
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
fireEvent.change(screen.getByLabelText('Repository'), {
|
|
738
|
+
target: { value: 'myorg/repo' },
|
|
739
|
+
});
|
|
740
|
+
fireEvent.change(screen.getByLabelText('Branch'), {
|
|
741
|
+
target: { value: 'main' },
|
|
742
|
+
});
|
|
743
|
+
|
|
744
|
+
fireEvent.click(screen.getByText('Save Settings'));
|
|
745
|
+
|
|
746
|
+
await waitFor(() => {
|
|
747
|
+
expect(mockDjClient.updateNamespaceGitConfig).toHaveBeenCalledWith(
|
|
748
|
+
'test.namespace',
|
|
749
|
+
expect.objectContaining({
|
|
750
|
+
github_repo_path: 'myorg/repo',
|
|
751
|
+
git_branch: 'main',
|
|
752
|
+
}),
|
|
753
|
+
);
|
|
754
|
+
});
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
it('should call createBranch when creating a branch', async () => {
|
|
758
|
+
const mockDjClient = {
|
|
759
|
+
namespaceSources: jest.fn().mockResolvedValue({
|
|
760
|
+
total_deployments: 1,
|
|
761
|
+
primary_source: {
|
|
762
|
+
type: 'git',
|
|
763
|
+
repository: 'test/repo',
|
|
764
|
+
branch: 'main',
|
|
765
|
+
},
|
|
766
|
+
}),
|
|
767
|
+
listDeployments: jest.fn().mockResolvedValue([]),
|
|
768
|
+
getNamespaceGitConfig: jest.fn().mockResolvedValue({
|
|
769
|
+
github_repo_path: 'test/repo',
|
|
770
|
+
git_branch: 'main',
|
|
771
|
+
git_path: 'nodes/',
|
|
772
|
+
git_only: false,
|
|
773
|
+
}),
|
|
774
|
+
createBranch: jest.fn().mockResolvedValue({
|
|
775
|
+
branch: {
|
|
776
|
+
namespace: 'test.feature_xyz',
|
|
777
|
+
git_branch: 'feature-xyz',
|
|
778
|
+
parent_namespace: 'test.namespace',
|
|
779
|
+
},
|
|
780
|
+
deployment_results: [],
|
|
781
|
+
}),
|
|
782
|
+
};
|
|
783
|
+
|
|
784
|
+
render(
|
|
785
|
+
<MemoryRouter>
|
|
786
|
+
<DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
|
|
787
|
+
<NamespaceHeader namespace="test.namespace" />
|
|
788
|
+
</DJClientContext.Provider>
|
|
789
|
+
</MemoryRouter>,
|
|
790
|
+
);
|
|
791
|
+
|
|
792
|
+
await waitFor(() => {
|
|
793
|
+
expect(screen.getByText('New Branch')).toBeInTheDocument();
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
fireEvent.click(screen.getByText('New Branch'));
|
|
797
|
+
|
|
798
|
+
await waitFor(() => {
|
|
799
|
+
expect(screen.getByLabelText('Branch Name')).toBeInTheDocument();
|
|
800
|
+
});
|
|
801
|
+
|
|
802
|
+
fireEvent.change(screen.getByLabelText('Branch Name'), {
|
|
803
|
+
target: { value: 'feature-xyz' },
|
|
804
|
+
});
|
|
805
|
+
|
|
806
|
+
// The button inside the modal is labeled "Create Branch"
|
|
807
|
+
fireEvent.click(screen.getByRole('button', { name: 'Create Branch' }));
|
|
808
|
+
|
|
809
|
+
await waitFor(() => {
|
|
810
|
+
expect(mockDjClient.createBranch).toHaveBeenCalledWith(
|
|
811
|
+
'test.namespace',
|
|
812
|
+
'feature-xyz',
|
|
813
|
+
);
|
|
814
|
+
});
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
it('should call syncNamespaceToGit when syncing', async () => {
|
|
818
|
+
// Sync to Git only shows for branch namespaces
|
|
819
|
+
const mockDjClient = {
|
|
820
|
+
namespaceSources: jest.fn().mockResolvedValue({
|
|
821
|
+
total_deployments: 1,
|
|
822
|
+
primary_source: {
|
|
823
|
+
type: 'git',
|
|
824
|
+
repository: 'test/repo',
|
|
825
|
+
branch: 'feature',
|
|
826
|
+
},
|
|
827
|
+
}),
|
|
828
|
+
listDeployments: jest.fn().mockResolvedValue([]),
|
|
829
|
+
getNamespaceGitConfig: jest
|
|
830
|
+
.fn()
|
|
831
|
+
.mockResolvedValueOnce({
|
|
832
|
+
github_repo_path: 'test/repo',
|
|
833
|
+
git_branch: 'feature',
|
|
834
|
+
git_path: 'nodes/',
|
|
835
|
+
git_only: false,
|
|
836
|
+
parent_namespace: 'test.main',
|
|
837
|
+
})
|
|
838
|
+
.mockResolvedValueOnce({
|
|
839
|
+
github_repo_path: 'test/repo',
|
|
840
|
+
git_branch: 'main',
|
|
841
|
+
git_path: 'nodes/',
|
|
842
|
+
}),
|
|
843
|
+
getPullRequest: jest.fn().mockResolvedValue(null),
|
|
844
|
+
syncNamespaceToGit: jest.fn().mockResolvedValue({
|
|
845
|
+
files_synced: 5,
|
|
846
|
+
commit_sha: 'abc123',
|
|
847
|
+
commit_url: 'https://github.com/test/repo/commit/abc123',
|
|
848
|
+
}),
|
|
849
|
+
};
|
|
850
|
+
|
|
851
|
+
render(
|
|
852
|
+
<MemoryRouter>
|
|
853
|
+
<DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
|
|
854
|
+
<NamespaceHeader namespace="test.feature" />
|
|
855
|
+
</DJClientContext.Provider>
|
|
856
|
+
</MemoryRouter>,
|
|
857
|
+
);
|
|
858
|
+
|
|
859
|
+
await waitFor(() => {
|
|
860
|
+
expect(screen.getByText('Sync to Git')).toBeInTheDocument();
|
|
861
|
+
});
|
|
862
|
+
|
|
863
|
+
fireEvent.click(screen.getByText('Sync to Git'));
|
|
864
|
+
|
|
865
|
+
await waitFor(() => {
|
|
866
|
+
expect(screen.getByLabelText(/Commit Message/)).toBeInTheDocument();
|
|
867
|
+
});
|
|
868
|
+
|
|
869
|
+
fireEvent.change(screen.getByLabelText(/Commit Message/), {
|
|
870
|
+
target: { value: 'Test commit' },
|
|
871
|
+
});
|
|
872
|
+
|
|
873
|
+
fireEvent.click(screen.getByRole('button', { name: 'Sync Now' }));
|
|
874
|
+
|
|
875
|
+
await waitFor(() => {
|
|
876
|
+
expect(mockDjClient.syncNamespaceToGit).toHaveBeenCalledWith(
|
|
877
|
+
'test.feature',
|
|
878
|
+
'Test commit',
|
|
879
|
+
);
|
|
880
|
+
});
|
|
881
|
+
});
|
|
882
|
+
|
|
883
|
+
it('should show View PR button when PR exists', async () => {
|
|
884
|
+
const mockDjClient = {
|
|
885
|
+
namespaceSources: jest.fn().mockResolvedValue({
|
|
886
|
+
total_deployments: 1,
|
|
887
|
+
primary_source: {
|
|
888
|
+
type: 'git',
|
|
889
|
+
repository: 'test/repo',
|
|
890
|
+
branch: 'feature',
|
|
891
|
+
},
|
|
892
|
+
}),
|
|
893
|
+
listDeployments: jest.fn().mockResolvedValue([]),
|
|
894
|
+
getNamespaceGitConfig: jest.fn().mockResolvedValue({
|
|
895
|
+
github_repo_path: 'test/repo',
|
|
896
|
+
git_branch: 'feature',
|
|
897
|
+
git_path: 'nodes/',
|
|
898
|
+
git_only: false,
|
|
899
|
+
parent_namespace: 'test.main',
|
|
900
|
+
}),
|
|
901
|
+
getPullRequest: jest.fn().mockResolvedValue({
|
|
902
|
+
pr_number: 42,
|
|
903
|
+
pr_url: 'https://github.com/test/repo/pull/42',
|
|
904
|
+
}),
|
|
905
|
+
};
|
|
906
|
+
|
|
907
|
+
render(
|
|
908
|
+
<MemoryRouter>
|
|
909
|
+
<DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
|
|
910
|
+
<NamespaceHeader namespace="test.feature" />
|
|
911
|
+
</DJClientContext.Provider>
|
|
912
|
+
</MemoryRouter>,
|
|
913
|
+
);
|
|
914
|
+
|
|
915
|
+
await waitFor(() => {
|
|
916
|
+
expect(screen.getByText(/View PR #42/)).toBeInTheDocument();
|
|
917
|
+
});
|
|
918
|
+
});
|
|
919
|
+
|
|
920
|
+
it('should call createPullRequest when creating a PR', async () => {
|
|
921
|
+
const mockDjClient = {
|
|
922
|
+
namespaceSources: jest.fn().mockResolvedValue({
|
|
923
|
+
total_deployments: 1,
|
|
924
|
+
primary_source: {
|
|
925
|
+
type: 'git',
|
|
926
|
+
repository: 'test/repo',
|
|
927
|
+
branch: 'feature',
|
|
928
|
+
},
|
|
929
|
+
}),
|
|
930
|
+
listDeployments: jest.fn().mockResolvedValue([]),
|
|
931
|
+
getNamespaceGitConfig: jest
|
|
932
|
+
.fn()
|
|
933
|
+
.mockResolvedValueOnce({
|
|
934
|
+
github_repo_path: 'test/repo',
|
|
935
|
+
git_branch: 'feature',
|
|
936
|
+
git_path: 'nodes/',
|
|
937
|
+
git_only: false,
|
|
938
|
+
parent_namespace: 'test.main',
|
|
939
|
+
})
|
|
940
|
+
.mockResolvedValueOnce({
|
|
941
|
+
github_repo_path: 'test/repo',
|
|
942
|
+
git_branch: 'main',
|
|
943
|
+
git_path: 'nodes/',
|
|
944
|
+
}),
|
|
945
|
+
getPullRequest: jest.fn().mockResolvedValue(null),
|
|
946
|
+
syncNamespaceToGit: jest.fn().mockResolvedValue({
|
|
947
|
+
files_synced: 3,
|
|
948
|
+
commit_sha: 'abc123',
|
|
949
|
+
commit_url: 'https://github.com/test/repo/commit/abc123',
|
|
950
|
+
}),
|
|
951
|
+
createPullRequest: jest.fn().mockResolvedValue({
|
|
952
|
+
pr_number: 99,
|
|
953
|
+
pr_url: 'https://github.com/test/repo/pull/99',
|
|
954
|
+
head_branch: 'feature',
|
|
955
|
+
base_branch: 'main',
|
|
956
|
+
}),
|
|
957
|
+
};
|
|
958
|
+
|
|
959
|
+
render(
|
|
960
|
+
<MemoryRouter>
|
|
961
|
+
<DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
|
|
962
|
+
<NamespaceHeader namespace="test.feature" />
|
|
963
|
+
</DJClientContext.Provider>
|
|
964
|
+
</MemoryRouter>,
|
|
965
|
+
);
|
|
966
|
+
|
|
967
|
+
await waitFor(() => {
|
|
968
|
+
expect(screen.getByText('Create PR')).toBeInTheDocument();
|
|
969
|
+
});
|
|
970
|
+
|
|
971
|
+
fireEvent.click(screen.getByText('Create PR'));
|
|
972
|
+
|
|
973
|
+
await waitFor(() => {
|
|
974
|
+
expect(screen.getByLabelText(/Title/)).toBeInTheDocument();
|
|
975
|
+
});
|
|
976
|
+
|
|
977
|
+
fireEvent.change(screen.getByLabelText(/Title/), {
|
|
978
|
+
target: { value: 'My PR Title' },
|
|
979
|
+
});
|
|
980
|
+
fireEvent.change(screen.getByLabelText(/Description/), {
|
|
981
|
+
target: { value: 'PR description' },
|
|
982
|
+
});
|
|
983
|
+
|
|
984
|
+
// There are two "Create PR" buttons - one in header, one in modal
|
|
985
|
+
// Get all and click the last one (modal's submit button)
|
|
986
|
+
const createPRButtons = screen.getAllByRole('button', {
|
|
987
|
+
name: 'Create PR',
|
|
988
|
+
});
|
|
989
|
+
fireEvent.click(createPRButtons[createPRButtons.length - 1]);
|
|
990
|
+
|
|
991
|
+
await waitFor(() => {
|
|
992
|
+
expect(mockDjClient.syncNamespaceToGit).toHaveBeenCalledWith(
|
|
993
|
+
'test.feature',
|
|
994
|
+
'My PR Title',
|
|
995
|
+
);
|
|
996
|
+
});
|
|
997
|
+
|
|
998
|
+
await waitFor(() => {
|
|
999
|
+
expect(mockDjClient.createPullRequest).toHaveBeenCalledWith(
|
|
1000
|
+
'test.feature',
|
|
1001
|
+
'My PR Title',
|
|
1002
|
+
'PR description',
|
|
1003
|
+
);
|
|
1004
|
+
});
|
|
1005
|
+
});
|
|
1006
|
+
|
|
1007
|
+
it('should call deleteBranch when deleting a branch', async () => {
|
|
1008
|
+
const mockDjClient = {
|
|
1009
|
+
namespaceSources: jest.fn().mockResolvedValue({
|
|
1010
|
+
total_deployments: 1,
|
|
1011
|
+
primary_source: {
|
|
1012
|
+
type: 'git',
|
|
1013
|
+
repository: 'test/repo',
|
|
1014
|
+
branch: 'feature',
|
|
1015
|
+
},
|
|
1016
|
+
}),
|
|
1017
|
+
listDeployments: jest.fn().mockResolvedValue([]),
|
|
1018
|
+
getNamespaceGitConfig: jest
|
|
1019
|
+
.fn()
|
|
1020
|
+
.mockResolvedValueOnce({
|
|
1021
|
+
github_repo_path: 'test/repo',
|
|
1022
|
+
git_branch: 'feature',
|
|
1023
|
+
git_path: 'nodes/',
|
|
1024
|
+
git_only: false,
|
|
1025
|
+
parent_namespace: 'test.main',
|
|
1026
|
+
})
|
|
1027
|
+
.mockResolvedValueOnce({
|
|
1028
|
+
github_repo_path: 'test/repo',
|
|
1029
|
+
git_branch: 'main',
|
|
1030
|
+
git_path: 'nodes/',
|
|
1031
|
+
}),
|
|
1032
|
+
getPullRequest: jest.fn().mockResolvedValue(null),
|
|
1033
|
+
deleteBranch: jest.fn().mockResolvedValue({ success: true }),
|
|
1034
|
+
};
|
|
1035
|
+
|
|
1036
|
+
// Mock window.location
|
|
1037
|
+
delete window.location;
|
|
1038
|
+
window.location = { href: '' };
|
|
1039
|
+
|
|
1040
|
+
render(
|
|
1041
|
+
<MemoryRouter>
|
|
1042
|
+
<DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
|
|
1043
|
+
<NamespaceHeader namespace="test.feature" />
|
|
1044
|
+
</DJClientContext.Provider>
|
|
1045
|
+
</MemoryRouter>,
|
|
1046
|
+
);
|
|
1047
|
+
|
|
1048
|
+
await waitFor(() => {
|
|
1049
|
+
// Delete Branch button in header only has icon with title attribute
|
|
1050
|
+
expect(screen.getByTitle('Delete Branch')).toBeInTheDocument();
|
|
1051
|
+
});
|
|
1052
|
+
|
|
1053
|
+
fireEvent.click(screen.getByTitle('Delete Branch'));
|
|
1054
|
+
|
|
1055
|
+
await waitFor(() => {
|
|
1056
|
+
expect(screen.getByRole('checkbox')).toBeInTheDocument();
|
|
1057
|
+
});
|
|
1058
|
+
|
|
1059
|
+
// There are two buttons with "Delete Branch" - header icon and modal button
|
|
1060
|
+
// Get all and click the last one (modal's submit button)
|
|
1061
|
+
const deleteBranchButtons = screen.getAllByRole('button', {
|
|
1062
|
+
name: 'Delete Branch',
|
|
1063
|
+
});
|
|
1064
|
+
fireEvent.click(deleteBranchButtons[deleteBranchButtons.length - 1]);
|
|
1065
|
+
|
|
1066
|
+
await waitFor(() => {
|
|
1067
|
+
expect(mockDjClient.deleteBranch).toHaveBeenCalledWith(
|
|
1068
|
+
'test.main',
|
|
1069
|
+
'test.feature',
|
|
1070
|
+
true,
|
|
1071
|
+
);
|
|
1072
|
+
});
|
|
1073
|
+
});
|
|
1074
|
+
|
|
1075
|
+
it('should fetch parent git config for branch namespace', async () => {
|
|
1076
|
+
const mockDjClient = {
|
|
1077
|
+
namespaceSources: jest.fn().mockResolvedValue({
|
|
1078
|
+
total_deployments: 1,
|
|
1079
|
+
primary_source: {
|
|
1080
|
+
type: 'git',
|
|
1081
|
+
repository: 'test/repo',
|
|
1082
|
+
branch: 'feature',
|
|
1083
|
+
},
|
|
1084
|
+
}),
|
|
1085
|
+
listDeployments: jest.fn().mockResolvedValue([]),
|
|
1086
|
+
getNamespaceGitConfig: jest
|
|
1087
|
+
.fn()
|
|
1088
|
+
.mockResolvedValueOnce({
|
|
1089
|
+
github_repo_path: 'test/repo',
|
|
1090
|
+
git_branch: 'feature',
|
|
1091
|
+
git_path: 'nodes/',
|
|
1092
|
+
git_only: false,
|
|
1093
|
+
parent_namespace: 'test.main',
|
|
1094
|
+
})
|
|
1095
|
+
.mockResolvedValueOnce({
|
|
1096
|
+
github_repo_path: 'test/repo',
|
|
1097
|
+
git_branch: 'main',
|
|
1098
|
+
git_path: 'nodes/',
|
|
1099
|
+
}),
|
|
1100
|
+
getPullRequest: jest.fn().mockResolvedValue(null),
|
|
1101
|
+
};
|
|
1102
|
+
|
|
1103
|
+
render(
|
|
1104
|
+
<MemoryRouter>
|
|
1105
|
+
<DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
|
|
1106
|
+
<NamespaceHeader namespace="test.feature" />
|
|
1107
|
+
</DJClientContext.Provider>
|
|
1108
|
+
</MemoryRouter>,
|
|
1109
|
+
);
|
|
1110
|
+
|
|
1111
|
+
await waitFor(() => {
|
|
1112
|
+
expect(mockDjClient.getNamespaceGitConfig).toHaveBeenCalledWith(
|
|
1113
|
+
'test.feature',
|
|
1114
|
+
);
|
|
1115
|
+
});
|
|
1116
|
+
|
|
1117
|
+
await waitFor(() => {
|
|
1118
|
+
expect(mockDjClient.getNamespaceGitConfig).toHaveBeenCalledWith(
|
|
1119
|
+
'test.main',
|
|
1120
|
+
);
|
|
1121
|
+
});
|
|
1122
|
+
|
|
1123
|
+
await waitFor(() => {
|
|
1124
|
+
expect(mockDjClient.getPullRequest).toHaveBeenCalledWith('test.feature');
|
|
1125
|
+
});
|
|
1126
|
+
});
|
|
1127
|
+
|
|
1128
|
+
it('should handle error fetching parent git config gracefully', async () => {
|
|
1129
|
+
const consoleSpy = jest.spyOn(console, 'error').mockImplementation();
|
|
1130
|
+
|
|
1131
|
+
const mockDjClient = {
|
|
1132
|
+
namespaceSources: jest.fn().mockResolvedValue({
|
|
1133
|
+
total_deployments: 1,
|
|
1134
|
+
primary_source: {
|
|
1135
|
+
type: 'git',
|
|
1136
|
+
repository: 'test/repo',
|
|
1137
|
+
branch: 'feature',
|
|
1138
|
+
},
|
|
1139
|
+
}),
|
|
1140
|
+
listDeployments: jest.fn().mockResolvedValue([]),
|
|
1141
|
+
getNamespaceGitConfig: jest
|
|
1142
|
+
.fn()
|
|
1143
|
+
.mockResolvedValueOnce({
|
|
1144
|
+
github_repo_path: 'test/repo',
|
|
1145
|
+
git_branch: 'feature',
|
|
1146
|
+
git_path: 'nodes/',
|
|
1147
|
+
git_only: false,
|
|
1148
|
+
parent_namespace: 'test.main',
|
|
1149
|
+
})
|
|
1150
|
+
.mockRejectedValueOnce(new Error('Parent not found')),
|
|
1151
|
+
getPullRequest: jest.fn().mockResolvedValue(null),
|
|
1152
|
+
};
|
|
1153
|
+
|
|
1154
|
+
render(
|
|
1155
|
+
<MemoryRouter>
|
|
1156
|
+
<DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
|
|
1157
|
+
<NamespaceHeader namespace="test.feature" />
|
|
1158
|
+
</DJClientContext.Provider>
|
|
1159
|
+
</MemoryRouter>,
|
|
1160
|
+
);
|
|
1161
|
+
|
|
1162
|
+
await waitFor(() => {
|
|
1163
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
1164
|
+
'Failed to fetch parent git config:',
|
|
1165
|
+
expect.any(Error),
|
|
1166
|
+
);
|
|
1167
|
+
});
|
|
1168
|
+
|
|
1169
|
+
consoleSpy.mockRestore();
|
|
1170
|
+
});
|
|
1171
|
+
|
|
1172
|
+
it('should handle error fetching PR gracefully', async () => {
|
|
1173
|
+
const mockDjClient = {
|
|
1174
|
+
namespaceSources: jest.fn().mockResolvedValue({
|
|
1175
|
+
total_deployments: 1,
|
|
1176
|
+
primary_source: {
|
|
1177
|
+
type: 'git',
|
|
1178
|
+
repository: 'test/repo',
|
|
1179
|
+
branch: 'feature',
|
|
1180
|
+
},
|
|
1181
|
+
}),
|
|
1182
|
+
listDeployments: jest.fn().mockResolvedValue([]),
|
|
1183
|
+
getNamespaceGitConfig: jest
|
|
1184
|
+
.fn()
|
|
1185
|
+
.mockResolvedValueOnce({
|
|
1186
|
+
github_repo_path: 'test/repo',
|
|
1187
|
+
git_branch: 'feature',
|
|
1188
|
+
git_path: 'nodes/',
|
|
1189
|
+
git_only: false,
|
|
1190
|
+
parent_namespace: 'test.main',
|
|
1191
|
+
})
|
|
1192
|
+
.mockResolvedValueOnce({
|
|
1193
|
+
github_repo_path: 'test/repo',
|
|
1194
|
+
git_branch: 'main',
|
|
1195
|
+
git_path: 'nodes/',
|
|
1196
|
+
}),
|
|
1197
|
+
getPullRequest: jest.fn().mockRejectedValue(new Error('API Error')),
|
|
1198
|
+
};
|
|
1199
|
+
|
|
1200
|
+
render(
|
|
1201
|
+
<MemoryRouter>
|
|
1202
|
+
<DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
|
|
1203
|
+
<NamespaceHeader namespace="test.feature" />
|
|
1204
|
+
</DJClientContext.Provider>
|
|
1205
|
+
</MemoryRouter>,
|
|
1206
|
+
);
|
|
1207
|
+
|
|
1208
|
+
// Should render without crashing and show Create PR button
|
|
1209
|
+
await waitFor(() => {
|
|
1210
|
+
expect(screen.getByText('Create PR')).toBeInTheDocument();
|
|
1211
|
+
});
|
|
1212
|
+
});
|
|
1213
|
+
|
|
1214
|
+
it('should call onGitConfigLoaded callback when config is fetched', async () => {
|
|
1215
|
+
const onGitConfigLoaded = jest.fn();
|
|
1216
|
+
const mockDjClient = {
|
|
1217
|
+
namespaceSources: jest.fn().mockResolvedValue({
|
|
1218
|
+
total_deployments: 0,
|
|
1219
|
+
primary_source: null,
|
|
1220
|
+
}),
|
|
1221
|
+
listDeployments: jest.fn().mockResolvedValue([]),
|
|
1222
|
+
getNamespaceGitConfig: jest.fn().mockResolvedValue({
|
|
1223
|
+
github_repo_path: 'test/repo',
|
|
1224
|
+
git_branch: 'main',
|
|
1225
|
+
}),
|
|
1226
|
+
};
|
|
1227
|
+
|
|
1228
|
+
render(
|
|
1229
|
+
<MemoryRouter>
|
|
1230
|
+
<DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
|
|
1231
|
+
<NamespaceHeader
|
|
1232
|
+
namespace="test.namespace"
|
|
1233
|
+
onGitConfigLoaded={onGitConfigLoaded}
|
|
1234
|
+
/>
|
|
1235
|
+
</DJClientContext.Provider>
|
|
1236
|
+
</MemoryRouter>,
|
|
1237
|
+
);
|
|
1238
|
+
|
|
1239
|
+
await waitFor(() => {
|
|
1240
|
+
expect(onGitConfigLoaded).toHaveBeenCalledWith({
|
|
1241
|
+
github_repo_path: 'test/repo',
|
|
1242
|
+
git_branch: 'main',
|
|
1243
|
+
});
|
|
1244
|
+
});
|
|
1245
|
+
});
|
|
1246
|
+
|
|
1247
|
+
it('should call onGitConfigLoaded with null when git config fetch fails', async () => {
|
|
1248
|
+
const onGitConfigLoaded = jest.fn();
|
|
1249
|
+
const mockDjClient = {
|
|
1250
|
+
namespaceSources: jest.fn().mockResolvedValue({
|
|
1251
|
+
total_deployments: 0,
|
|
1252
|
+
primary_source: null,
|
|
1253
|
+
}),
|
|
1254
|
+
listDeployments: jest.fn().mockResolvedValue([]),
|
|
1255
|
+
getNamespaceGitConfig: jest
|
|
1256
|
+
.fn()
|
|
1257
|
+
.mockRejectedValue(new Error('Config not found')),
|
|
1258
|
+
};
|
|
1259
|
+
|
|
1260
|
+
render(
|
|
1261
|
+
<MemoryRouter>
|
|
1262
|
+
<DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
|
|
1263
|
+
<NamespaceHeader
|
|
1264
|
+
namespace="test.namespace"
|
|
1265
|
+
onGitConfigLoaded={onGitConfigLoaded}
|
|
1266
|
+
/>
|
|
1267
|
+
</DJClientContext.Provider>
|
|
1268
|
+
</MemoryRouter>,
|
|
1269
|
+
);
|
|
1270
|
+
|
|
1271
|
+
await waitFor(() => {
|
|
1272
|
+
expect(onGitConfigLoaded).toHaveBeenCalledWith(null);
|
|
1273
|
+
});
|
|
1274
|
+
});
|
|
1275
|
+
|
|
1276
|
+
it('should call deleteNamespaceGitConfig when removing git settings', async () => {
|
|
1277
|
+
// Mock window.confirm for this test
|
|
1278
|
+
global.confirm = jest.fn(() => true);
|
|
1279
|
+
|
|
1280
|
+
const mockDjClient = {
|
|
1281
|
+
namespaceSources: jest.fn().mockResolvedValue({
|
|
1282
|
+
total_deployments: 0,
|
|
1283
|
+
primary_source: null,
|
|
1284
|
+
}),
|
|
1285
|
+
listDeployments: jest.fn().mockResolvedValue([]),
|
|
1286
|
+
getNamespaceGitConfig: jest.fn().mockResolvedValue({
|
|
1287
|
+
github_repo_path: 'test/repo',
|
|
1288
|
+
git_branch: 'main',
|
|
1289
|
+
git_path: 'nodes/',
|
|
1290
|
+
git_only: false,
|
|
1291
|
+
}),
|
|
1292
|
+
deleteNamespaceGitConfig: jest.fn().mockResolvedValue({ success: true }),
|
|
1293
|
+
};
|
|
1294
|
+
|
|
1295
|
+
render(
|
|
1296
|
+
<MemoryRouter>
|
|
1297
|
+
<DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
|
|
1298
|
+
<NamespaceHeader namespace="test.namespace" />
|
|
1299
|
+
</DJClientContext.Provider>
|
|
1300
|
+
</MemoryRouter>,
|
|
1301
|
+
);
|
|
1302
|
+
|
|
1303
|
+
await waitFor(() => {
|
|
1304
|
+
expect(screen.getByText('Git Settings')).toBeInTheDocument();
|
|
1305
|
+
});
|
|
1306
|
+
|
|
1307
|
+
fireEvent.click(screen.getByText('Git Settings'));
|
|
1308
|
+
|
|
1309
|
+
await waitFor(() => {
|
|
1310
|
+
expect(screen.getByText('Git Configuration')).toBeInTheDocument();
|
|
1311
|
+
});
|
|
1312
|
+
|
|
1313
|
+
// Click reset button in the modal (button text is "Reset")
|
|
1314
|
+
const removeButton = screen.getByText('Reset');
|
|
1315
|
+
fireEvent.click(removeButton);
|
|
1316
|
+
|
|
1317
|
+
await waitFor(() => {
|
|
1318
|
+
expect(mockDjClient.deleteNamespaceGitConfig).toHaveBeenCalledWith(
|
|
1319
|
+
'test.namespace',
|
|
1320
|
+
);
|
|
1321
|
+
});
|
|
1322
|
+
|
|
1323
|
+
// Clean up mock
|
|
1324
|
+
jest.restoreAllMocks();
|
|
1325
|
+
});
|
|
1326
|
+
|
|
1327
|
+
it('should handle sync error in handleCreatePR', async () => {
|
|
1328
|
+
const mockDjClient = {
|
|
1329
|
+
namespaceSources: jest.fn().mockResolvedValue({
|
|
1330
|
+
total_deployments: 1,
|
|
1331
|
+
primary_source: {
|
|
1332
|
+
type: 'git',
|
|
1333
|
+
repository: 'test/repo',
|
|
1334
|
+
branch: 'feature',
|
|
1335
|
+
},
|
|
1336
|
+
}),
|
|
1337
|
+
listDeployments: jest.fn().mockResolvedValue([]),
|
|
1338
|
+
getNamespaceGitConfig: jest
|
|
1339
|
+
.fn()
|
|
1340
|
+
.mockResolvedValueOnce({
|
|
1341
|
+
github_repo_path: 'test/repo',
|
|
1342
|
+
git_branch: 'feature',
|
|
1343
|
+
git_path: 'nodes/',
|
|
1344
|
+
git_only: false,
|
|
1345
|
+
parent_namespace: 'test.main',
|
|
1346
|
+
})
|
|
1347
|
+
.mockResolvedValueOnce({
|
|
1348
|
+
github_repo_path: 'test/repo',
|
|
1349
|
+
git_branch: 'main',
|
|
1350
|
+
git_path: 'nodes/',
|
|
1351
|
+
}),
|
|
1352
|
+
getPullRequest: jest.fn().mockResolvedValue(null),
|
|
1353
|
+
syncNamespaceToGit: jest.fn().mockResolvedValue({
|
|
1354
|
+
_error: true,
|
|
1355
|
+
message: 'Sync failed: merge conflict',
|
|
1356
|
+
}),
|
|
1357
|
+
};
|
|
1358
|
+
|
|
1359
|
+
render(
|
|
1360
|
+
<MemoryRouter>
|
|
1361
|
+
<DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
|
|
1362
|
+
<NamespaceHeader namespace="test.feature" />
|
|
1363
|
+
</DJClientContext.Provider>
|
|
1364
|
+
</MemoryRouter>,
|
|
1365
|
+
);
|
|
1366
|
+
|
|
1367
|
+
await waitFor(() => {
|
|
1368
|
+
expect(screen.getByText('Create PR')).toBeInTheDocument();
|
|
1369
|
+
});
|
|
1370
|
+
|
|
1371
|
+
fireEvent.click(screen.getByText('Create PR'));
|
|
1372
|
+
|
|
1373
|
+
await waitFor(() => {
|
|
1374
|
+
expect(screen.getByLabelText(/Title/)).toBeInTheDocument();
|
|
1375
|
+
});
|
|
1376
|
+
|
|
1377
|
+
fireEvent.change(screen.getByLabelText(/Title/), {
|
|
1378
|
+
target: { value: 'My PR Title' },
|
|
1379
|
+
});
|
|
1380
|
+
|
|
1381
|
+
const createPRButtons = screen.getAllByRole('button', {
|
|
1382
|
+
name: 'Create PR',
|
|
1383
|
+
});
|
|
1384
|
+
fireEvent.click(createPRButtons[createPRButtons.length - 1]);
|
|
1385
|
+
|
|
1386
|
+
await waitFor(() => {
|
|
1387
|
+
expect(mockDjClient.syncNamespaceToGit).toHaveBeenCalledWith(
|
|
1388
|
+
'test.feature',
|
|
1389
|
+
'My PR Title',
|
|
1390
|
+
);
|
|
1391
|
+
});
|
|
1392
|
+
|
|
1393
|
+
// Should show error message from sync failure
|
|
1394
|
+
await waitFor(() => {
|
|
1395
|
+
expect(
|
|
1396
|
+
screen.getByText(/Sync failed: merge conflict/),
|
|
1397
|
+
).toBeInTheDocument();
|
|
1398
|
+
});
|
|
1399
|
+
});
|
|
511
1400
|
});
|