Skip to content

Commit e69c09d

Browse files
committed
fix(onboarding): Update tests for mount-time repo fetch
Tests expected the old search-driven behavior where data only loaded after typing. Now that repos fetch on mount, update mocks and assertions to match. Add a fallback in ScmVirtualizedMenuList for when the virtualizer cannot measure the container (jsdom has no layout engine).
1 parent 73ba739 commit e69c09d

File tree

3 files changed

+40
-15
lines changed

3 files changed

+40
-15
lines changed

static/app/views/onboarding/components/scmRepoSelector.spec.tsx

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ describe('ScmRepoSelector', () => {
4545
});
4646

4747
it('renders search placeholder', () => {
48+
MockApiClient.addMockResponse({
49+
url: `/organizations/${organization.slug}/integrations/${mockIntegration.id}/repos/`,
50+
body: {repos: []},
51+
});
52+
4853
render(<ScmRepoSelector integration={mockIntegration} />, {
4954
organization,
5055
wrapper: makeOnboardingWrapper(),
@@ -53,7 +58,7 @@ describe('ScmRepoSelector', () => {
5358
expect(screen.getByText('Search repositories')).toBeInTheDocument();
5459
});
5560

56-
it('shows empty state message when search returns no results', async () => {
61+
it('shows empty state message when no repos are available', async () => {
5762
MockApiClient.addMockResponse({
5863
url: `/organizations/${organization.slug}/integrations/${mockIntegration.id}/repos/`,
5964
body: {repos: []},
@@ -64,7 +69,7 @@ describe('ScmRepoSelector', () => {
6469
wrapper: makeOnboardingWrapper(),
6570
});
6671

67-
await userEvent.type(screen.getByRole('textbox'), 'nonexistent');
72+
await userEvent.click(screen.getByRole('textbox'));
6873

6974
expect(
7075
await screen.findByText(
@@ -85,14 +90,14 @@ describe('ScmRepoSelector', () => {
8590
wrapper: makeOnboardingWrapper(),
8691
});
8792

88-
await userEvent.type(screen.getByRole('textbox'), 'sentry');
93+
await userEvent.click(screen.getByRole('textbox'));
8994

9095
expect(
91-
await screen.findByText('Failed to search repositories. Please try again.')
96+
await screen.findByText('Failed to load repositories. Please try again.')
9297
).toBeInTheDocument();
9398
});
9499

95-
it('displays repos returned by search', async () => {
100+
it('displays repos fetched on mount', async () => {
96101
MockApiClient.addMockResponse({
97102
url: `/organizations/${organization.slug}/integrations/${mockIntegration.id}/repos/`,
98103
body: {
@@ -108,7 +113,7 @@ describe('ScmRepoSelector', () => {
108113
wrapper: makeOnboardingWrapper(),
109114
});
110115

111-
await userEvent.type(screen.getByRole('textbox'), 'get');
116+
await userEvent.click(screen.getByRole('textbox'));
112117

113118
expect(
114119
await screen.findByRole('menuitemradio', {name: 'sentry'})
@@ -117,6 +122,11 @@ describe('ScmRepoSelector', () => {
117122
});
118123

119124
it('shows selected repo value when one is in context', () => {
125+
MockApiClient.addMockResponse({
126+
url: `/organizations/${organization.slug}/integrations/${mockIntegration.id}/repos/`,
127+
body: {repos: []},
128+
});
129+
120130
const selectedRepo = RepositoryFixture({
121131
name: 'getsentry/old-repo',
122132
externalSlug: 'getsentry/old-repo',
@@ -130,7 +140,7 @@ describe('ScmRepoSelector', () => {
130140
expect(screen.getByText('getsentry/old-repo')).toBeInTheDocument();
131141
});
132142

133-
it('selects a repo from search results and triggers repo lookup', async () => {
143+
it('selects a repo and triggers repo lookup', async () => {
134144
MockApiClient.addMockResponse({
135145
url: `/organizations/${organization.slug}/integrations/${mockIntegration.id}/repos/`,
136146
body: {
@@ -153,13 +163,18 @@ describe('ScmRepoSelector', () => {
153163
wrapper: makeOnboardingWrapper(),
154164
});
155165

156-
await userEvent.type(screen.getByRole('textbox'), 'get');
166+
await userEvent.click(screen.getByRole('textbox'));
157167
await userEvent.click(await screen.findByRole('menuitemradio', {name: 'sentry'}));
158168

159169
await waitFor(() => expect(reposLookup).toHaveBeenCalled());
160170
});
161171

162172
it('clears the selected repo', async () => {
173+
MockApiClient.addMockResponse({
174+
url: `/organizations/${organization.slug}/integrations/${mockIntegration.id}/repos/`,
175+
body: {repos: []},
176+
});
177+
163178
const selectedRepo = RepositoryFixture({
164179
name: 'getsentry/old-repo',
165180
externalSlug: 'getsentry/old-repo',
@@ -172,16 +187,14 @@ describe('ScmRepoSelector', () => {
172187

173188
expect(screen.getByText('getsentry/old-repo')).toBeInTheDocument();
174189

175-
// The clear indicator uses Sentry's IconClose which renders with
176-
// role="img" rather than aria-hidden, so use the test-id directly.
177-
await userEvent.click(screen.getByTestId('icon-close'));
190+
await userEvent.click(await screen.findByTestId('icon-close'));
178191

179192
await waitFor(() => {
180193
expect(screen.queryByText('getsentry/old-repo')).not.toBeInTheDocument();
181194
});
182195
});
183196

184-
it('does not duplicate selected repo when it appears in search results', async () => {
197+
it('does not duplicate selected repo when it appears in results', async () => {
185198
const selectedRepo = RepositoryFixture({
186199
name: 'getsentry/sentry',
187200
externalSlug: 'getsentry/sentry',
@@ -202,15 +215,14 @@ describe('ScmRepoSelector', () => {
202215
wrapper: makeOnboardingWrapper({selectedRepository: selectedRepo}),
203216
});
204217

205-
await userEvent.type(screen.getByRole('textbox'), 'get');
218+
await userEvent.click(screen.getByRole('textbox'));
206219

207-
// Wait for search results to arrive
208220
expect(await screen.findByRole('menuitemradio', {name: 'relay'})).toBeInTheDocument();
209221
expect(screen.getByRole('menuitemradio', {name: 'sentry'})).toBeInTheDocument();
210222

211223
// If the options-prepend logic fires incorrectly, it adds an extra option
212224
// with label 'getsentry/sentry' (selectedRepository.name) alongside the
213-
// search result option with label 'sentry' (repo.name).
225+
// result option with label 'sentry' (repo.name).
214226
expect(
215227
screen.queryByRole('menuitemradio', {name: 'getsentry/sentry'})
216228
).not.toBeInTheDocument();

static/app/views/onboarding/components/scmVirtualizedMenuList.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,15 @@ export function ScmVirtualizedMenuList({
5555
);
5656
}
5757

58+
// Fallback when the virtualizer can't measure the container (e.g. jsdom)
59+
if (virtualItems.length === 0 && items.length > 0) {
60+
return (
61+
<div ref={combinedRef} {...innerProps} style={{maxHeight, overflowY: 'auto'}}>
62+
{children}
63+
</div>
64+
);
65+
}
66+
5867
return (
5968
<div ref={combinedRef} {...innerProps} style={{maxHeight, overflowY: 'auto'}}>
6069
<div style={{height: virtualizer.getTotalSize(), position: 'relative'}}>

static/app/views/onboarding/onboarding.spec.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,10 @@ describe('Onboarding', () => {
724724
url: `/organizations/${scmOrganization.slug}/repos/`,
725725
body: [],
726726
});
727+
MockApiClient.addMockResponse({
728+
url: `/organizations/${scmOrganization.slug}/integrations/1/repos/`,
729+
body: {repos: []},
730+
});
727731

728732
renderOnboarding('scm-connect');
729733

0 commit comments

Comments
 (0)