Skip to content

Commit b377452

Browse files
authored
feat(compass-global-writes): sharding error state COMPASS-8349 (#6374)
1 parent 41a1414 commit b377452

16 files changed

+858
-551
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { css, spacing } from '@mongodb-js/compass-components';
2+
3+
export const bannerStyles = css({
4+
textAlign: 'justify',
5+
});
6+
7+
export const paragraphStyles = css({
8+
display: 'flex',
9+
flexDirection: 'column',
10+
gap: spacing[100],
11+
});
12+
13+
export const containerStyles = css({
14+
display: 'flex',
15+
flexDirection: 'column',
16+
gap: spacing[400],
17+
marginBottom: spacing[400],
18+
textAlign: 'justify',
19+
});
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
import React from 'react';
2+
import { expect } from 'chai';
3+
import { screen, userEvent } from '@mongodb-js/testing-library-compass';
4+
import {
5+
CreateShardKeyForm,
6+
type CreateShardKeyFormProps,
7+
} from './create-shard-key-form';
8+
import { renderWithStore } from '../../tests/create-store';
9+
import sinon from 'sinon';
10+
11+
function renderWithProps(props?: Partial<CreateShardKeyFormProps>) {
12+
return renderWithStore(
13+
<CreateShardKeyForm
14+
isSubmittingForSharding={false}
15+
isCancellingSharding={false}
16+
namespace="airbnb.test"
17+
onCreateShardKey={() => {}}
18+
{...props}
19+
/>
20+
);
21+
}
22+
23+
function setShardingKeyFieldValue(value: string) {
24+
const input = screen.getByLabelText('Second shard key field');
25+
expect(input).to.exist;
26+
userEvent.type(input, value);
27+
expect(input).to.have.value(value);
28+
userEvent.keyboard('{Escape}');
29+
30+
// For some reason, when running tests in electron mode, the value of
31+
// the input field is not being updated. This is a workaround to ensure
32+
// the value is being updated before clicking the submit button.
33+
userEvent.click(screen.getByText(value), undefined, {
34+
skipPointerEventsCheck: true,
35+
});
36+
}
37+
38+
describe('CreateShardKeyForm', function () {
39+
let onCreateShardKeySpy: sinon.SinonSpy;
40+
context('default', function () {
41+
beforeEach(async function () {
42+
onCreateShardKeySpy = sinon.spy();
43+
await renderWithProps({ onCreateShardKey: onCreateShardKeySpy });
44+
});
45+
46+
it('renders location form field as disabled', function () {
47+
expect(screen.getByLabelText('First shard key field')).to.have.attribute(
48+
'aria-disabled',
49+
'true'
50+
);
51+
});
52+
53+
it('does not allow user to submit when no second shard key is selected', function () {
54+
expect(screen.getByTestId('shard-collection-button')).to.have.attribute(
55+
'aria-disabled',
56+
'true'
57+
);
58+
59+
userEvent.click(screen.getByTestId('shard-collection-button'));
60+
expect(onCreateShardKeySpy.called).to.be.false;
61+
});
62+
63+
it('allows user to input second shard key and submit it', function () {
64+
setShardingKeyFieldValue('name');
65+
66+
userEvent.click(screen.getByTestId('shard-collection-button'));
67+
68+
expect(onCreateShardKeySpy.calledOnce).to.be.true;
69+
expect(onCreateShardKeySpy.firstCall.args[0]).to.deep.equal({
70+
customShardKey: 'name',
71+
isShardKeyUnique: false,
72+
isCustomShardKeyHashed: false,
73+
presplitHashedZones: false,
74+
numInitialChunks: null,
75+
});
76+
});
77+
78+
it('renders advanced options and radio buttons for: default, unique-index and hashed index', function () {
79+
const accordian = screen.getByText('Advanced Shard Key Configuration');
80+
expect(accordian).to.exist;
81+
82+
userEvent.click(accordian);
83+
84+
const defaultRadio = screen.getByLabelText('Default');
85+
const uniqueIndexRadio = screen.getByLabelText(
86+
'Use unique index as the shard key'
87+
);
88+
const hashedIndexRadio = screen.getByLabelText(
89+
'Use hashed index as the shard key'
90+
);
91+
92+
expect(defaultRadio).to.exist;
93+
expect(uniqueIndexRadio).to.exist;
94+
expect(hashedIndexRadio).to.exist;
95+
});
96+
97+
it('allows user to select unique index as shard key', function () {
98+
const accordian = screen.getByText('Advanced Shard Key Configuration');
99+
userEvent.click(accordian);
100+
101+
const uniqueIndexRadio = screen.getByLabelText(
102+
'Use unique index as the shard key'
103+
);
104+
userEvent.click(uniqueIndexRadio);
105+
106+
expect(uniqueIndexRadio).to.have.attribute('aria-checked', 'true');
107+
108+
setShardingKeyFieldValue('name');
109+
110+
userEvent.click(screen.getByTestId('shard-collection-button'));
111+
112+
expect(onCreateShardKeySpy.calledOnce).to.be.true;
113+
expect(onCreateShardKeySpy.firstCall.args[0]).to.deep.equal({
114+
customShardKey: 'name',
115+
isShardKeyUnique: true,
116+
isCustomShardKeyHashed: false,
117+
presplitHashedZones: false,
118+
numInitialChunks: null,
119+
});
120+
});
121+
122+
it('allows user to select hashed index as shard key with split-chunks option', function () {
123+
const accordian = screen.getByText('Advanced Shard Key Configuration');
124+
userEvent.click(accordian);
125+
126+
const hashedIndexRadio = screen.getByLabelText(
127+
'Use hashed index as the shard key'
128+
);
129+
userEvent.click(hashedIndexRadio);
130+
131+
expect(hashedIndexRadio).to.have.attribute('aria-checked', 'true');
132+
133+
setShardingKeyFieldValue('name');
134+
135+
// Check pre-split data
136+
userEvent.click(screen.getByTestId('presplit-data-checkbox'), undefined, {
137+
skipPointerEventsCheck: true,
138+
});
139+
140+
userEvent.click(screen.getByTestId('shard-collection-button'));
141+
142+
expect(onCreateShardKeySpy.calledOnce).to.be.true;
143+
expect(onCreateShardKeySpy.firstCall.args[0]).to.deep.equal({
144+
customShardKey: 'name',
145+
isShardKeyUnique: false,
146+
isCustomShardKeyHashed: true,
147+
presplitHashedZones: true,
148+
numInitialChunks: null,
149+
});
150+
});
151+
152+
it('allows user to select hashed index as shard key with all its options', function () {
153+
const accordian = screen.getByText('Advanced Shard Key Configuration');
154+
userEvent.click(accordian);
155+
156+
const hashedIndexRadio = screen.getByLabelText(
157+
'Use hashed index as the shard key'
158+
);
159+
userEvent.click(hashedIndexRadio);
160+
161+
expect(hashedIndexRadio).to.have.attribute('aria-checked', 'true');
162+
163+
setShardingKeyFieldValue('name');
164+
165+
// Check pre-split data
166+
userEvent.click(screen.getByTestId('presplit-data-checkbox'), undefined, {
167+
skipPointerEventsCheck: true,
168+
});
169+
170+
// Enter number of chunks
171+
userEvent.type(screen.getByTestId('chunks-per-shard-input'), '10');
172+
173+
userEvent.click(screen.getByTestId('shard-collection-button'));
174+
175+
expect(onCreateShardKeySpy.calledOnce).to.be.true;
176+
expect(onCreateShardKeySpy.firstCall.args[0]).to.deep.equal({
177+
customShardKey: 'name',
178+
isShardKeyUnique: false,
179+
isCustomShardKeyHashed: true,
180+
presplitHashedZones: true,
181+
numInitialChunks: 10,
182+
});
183+
});
184+
});
185+
186+
it('cannot be submitted when already submitting', async function () {
187+
onCreateShardKeySpy = sinon.spy();
188+
await renderWithProps({
189+
onCreateShardKey: onCreateShardKeySpy,
190+
isSubmittingForSharding: true,
191+
});
192+
setShardingKeyFieldValue('name');
193+
194+
userEvent.click(screen.getByTestId('shard-collection-button'));
195+
196+
expect(onCreateShardKeySpy.calledOnce).to.be.false;
197+
});
198+
199+
it('cannot be submitted when cancelling', async function () {
200+
onCreateShardKeySpy = sinon.spy();
201+
await renderWithProps({
202+
onCreateShardKey: onCreateShardKeySpy,
203+
isCancellingSharding: true,
204+
});
205+
setShardingKeyFieldValue('name');
206+
207+
userEvent.click(screen.getByTestId('shard-collection-button'));
208+
209+
expect(onCreateShardKeySpy.calledOnce).to.be.false;
210+
});
211+
});

0 commit comments

Comments
 (0)