1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-10-18 11:14:57 +02:00
unleash.unleash/frontend/src/component/segments/SegmentFormStepTwo.test.tsx
Thomas Heartman e466e72e0d
chore(1-3842): don't reorder constraint properties / make id's non-optional (#10160)
This PR takes two steps towards better constraint handling:

## New type: `IConstraintWithId`

Introduces a new type, `IConstraintWithId`. This is the same as an
`IConstraint`, except the constraint id property is required. The idea
is that the list of editable constraints should move towards using this
instead of just `IConstraint`. That should prevent us (on a type-level)
from seeing more of the same kind of errors we saw with the segment
constraints yesterday.

I don't want to go ahead and update all the upstream uses of this to
IConstraintWithId in this PR, so I'll look at that separately.

## API payload constraint replacer

Introduces an api payload constraint "replacer", which we can use for
[JSON.stringify's `replacer`
parameter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#the_replacer_parameter).
The current implementation works both for strategies and for segments
and has been added to edit + create forms for both of these resources.

This has a couple benefits:
1. We can clearly state exactly how we want them to be rendered,
including property order. I've decided to go with context -> operator ->
value(s) as the main one (check the screenie), as I believe this is the
most logical reading order.
2. We can exclude value/values (whichever one doesn't work with the
operator)
3. It doesn't matter how we treat constraints internally, we can still
present the payload how we want
4. Importantly: this only affects the stringification for the
user-facing API payload, so it's very low risk. It does not affect
anything that we actually send to the api.


Here's what it can look like with ordered properties:

<img width="392" alt="image"
src="https://github.com/user-attachments/assets/f46f77c8-0b5a-4ded-b13a-bb567df60bd3"
/>
2025-06-19 10:09:38 +02:00

66 lines
1.9 KiB
TypeScript

import { render } from 'utils/testRenderer';
import { screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { testServerRoute, testServerSetup } from 'utils/testServer';
import { SegmentFormStepTwo } from './SegmentFormStepTwo.tsx';
import { vi } from 'vitest';
import {
CREATE_SEGMENT,
UPDATE_PROJECT_SEGMENT,
} from 'component/providers/AccessProvider/permissions';
import type { IConstraintWithId } from 'interfaces/strategy.ts';
const server = testServerSetup();
const setupRoutes = () => {
testServerRoute(server, '/api/admin/context', [
{ name: 'userId' },
{ name: 'appName' },
{ name: 'environment' },
]);
testServerRoute(server, '/api/admin/ui-config', {
flags: {},
});
};
const defaultProps = {
project: undefined,
constraints: [] as IConstraintWithId[],
setConstraints: vi.fn(),
setCurrentStep: vi.fn(),
mode: 'create' as const,
};
describe('SegmentFormStepTwo', () => {
beforeEach(() => {
setupRoutes();
defaultProps.setConstraints.mockClear();
});
test('adding context field through autocomplete updates constraints list', async () => {
const user = userEvent.setup();
render(<SegmentFormStepTwo {...defaultProps} />, {
permissions: [
{ permission: CREATE_SEGMENT },
{ permission: UPDATE_PROJECT_SEGMENT },
],
});
const autocomplete =
await screen.findByPlaceholderText('Select a context');
await user.click(autocomplete);
await waitFor(() => {
expect(screen.getByText('userId')).toBeInTheDocument();
});
await user.click(screen.getByText('userId'));
await waitFor(() => {
expect(defaultProps.setConstraints).toHaveBeenCalled();
});
});
});