123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345 |
- import {ActorFixture} from 'sentry-fixture/actor';
- import {MemberFixture} from 'sentry-fixture/member';
- import {OrganizationFixture} from 'sentry-fixture/organization';
- import {ProjectFixture} from 'sentry-fixture/project';
- import {TeamFixture} from 'sentry-fixture/team';
- import {UptimeRuleFixture} from 'sentry-fixture/uptimeRule';
- import {fireEvent, render, screen, userEvent} from 'sentry-test/reactTestingLibrary';
- import selectEvent from 'sentry-test/selectEvent';
- import OrganizationStore from 'sentry/stores/organizationStore';
- import ProjectsStore from 'sentry/stores/projectsStore';
- import {UptimeAlertForm} from 'sentry/views/alerts/rules/uptime/uptimeAlertForm';
- describe('Uptime Alert Form', function () {
- const organization = OrganizationFixture();
- const project = ProjectFixture({environments: ['prod', 'dev']});
- beforeEach(function () {
- OrganizationStore.onUpdate(organization);
- ProjectsStore.loadInitialData([project]);
- MockApiClient.addMockResponse({
- url: '/organizations/org-slug/members/',
- body: [MemberFixture()],
- });
- MockApiClient.addMockResponse({
- url: '/organizations/org-slug/teams/',
- body: [TeamFixture()],
- });
- });
- function input(name: string) {
- return screen.getByRole('textbox', {name});
- }
- it('can create a new rule', async function () {
- render(<UptimeAlertForm organization={organization} project={project} />, {
- organization,
- });
- await screen.findByText('Configure Request');
- await selectEvent.select(input('Environment'), 'prod');
- const timeout = screen.getByRole('slider', {name: 'Timeout'});
- fireEvent.change(timeout, {target: {value: '10000'}});
- await userEvent.clear(input('URL'));
- await userEvent.type(input('URL'), 'http://example.com');
- await selectEvent.clearAll(input('Method'));
- await selectEvent.select(input('Method'), 'POST');
- await userEvent.clear(input('Body'));
- await userEvent.type(input('Body'), '{{"key": "value"}');
- await userEvent.type(input('Name of header 1'), 'X-Something');
- await userEvent.type(input('Value of X-Something'), 'Header Value');
- await userEvent.click(screen.getByRole('checkbox', {name: 'Allow Sampling'}));
- const name = input('Uptime rule name');
- await userEvent.clear(name);
- await userEvent.type(name, 'New Uptime Rule');
- await selectEvent.select(input('Owner'), 'Foo Bar');
- const updateMock = MockApiClient.addMockResponse({
- url: `/projects/${organization.slug}/${project.slug}/uptime/`,
- method: 'POST',
- });
- await userEvent.click(screen.getByRole('button', {name: 'Create Rule'}));
- expect(updateMock).toHaveBeenCalledWith(
- expect.anything(),
- expect.objectContaining({
- data: expect.objectContaining({
- environment: 'prod',
- name: 'New Uptime Rule',
- owner: 'user:1',
- url: 'http://example.com',
- method: 'POST',
- headers: [['X-Something', 'Header Value']],
- body: '{"key": "value"}',
- traceSampling: true,
- intervalSeconds: 60,
- timeoutMs: 10_000,
- }),
- })
- );
- });
- it('renders existing rule', async function () {
- const rule = UptimeRuleFixture({
- name: 'Existing Rule',
- environment: 'prod',
- projectSlug: project.slug,
- url: 'https://existing-url.com',
- method: 'POST',
- headers: [
- ['X-Test1', 'value 1'],
- ['X-Test2', 'value 2'],
- ],
- body: '{"key": "value"}',
- traceSampling: true,
- timeoutMs: 7500,
- owner: ActorFixture(),
- });
- render(
- <UptimeAlertForm organization={organization} project={project} rule={rule} />,
- {organization}
- );
- await screen.findByText('Configure Request');
- expect(input('Uptime rule name')).toHaveValue('Existing Rule');
- expect(input('URL')).toHaveValue('https://existing-url.com');
- expect(input('Body')).toHaveValue('{"key": "value"}');
- expect(input('Name of header 1')).toHaveValue('X-Test1');
- expect(input('Name of header 2')).toHaveValue('X-Test2');
- expect(input('Value of X-Test1')).toHaveValue('value 1');
- expect(input('Value of X-Test2')).toHaveValue('value 2');
- await selectEvent.openMenu(input('Method'));
- expect(screen.getByRole('menuitemradio', {name: 'POST'})).toBeChecked();
- await selectEvent.openMenu(input('Environment'));
- expect(screen.getByRole('menuitemradio', {name: 'prod'})).toBeChecked();
- expect(screen.getByRole('checkbox', {name: 'Allow Sampling'})).toBeChecked();
- expect(screen.getByRole('slider', {name: 'Timeout'})).toHaveValue('7500');
- });
- it('handles simple edits', async function () {
- // XXX(epurkhiser): This test covers the case where the formModel waws not
- // triggering the observer that updates the apiEndpoint url based on the
- // selected project for existing rules. The other tests all pass as the
- // triggered error state from clearing the fields causes the observer to be
- // called for the first time and correctly set the apiEndpoint.
- const rule = UptimeRuleFixture({
- name: 'Existing Rule',
- projectSlug: project.slug,
- url: 'https://existing-url.com',
- owner: ActorFixture(),
- });
- render(
- <UptimeAlertForm organization={organization} project={project} rule={rule} />,
- {organization}
- );
- await screen.findByText('Configure Request');
- await userEvent.type(input('URL'), '/test');
- const updateMock = MockApiClient.addMockResponse({
- url: `/projects/${organization.slug}/${project.slug}/uptime/${rule.id}/`,
- method: 'PUT',
- });
- await userEvent.click(screen.getByRole('button', {name: 'Save Rule'}));
- expect(updateMock).toHaveBeenCalledWith(
- expect.anything(),
- expect.objectContaining({
- data: expect.objectContaining({
- url: 'https://existing-url.com/test',
- }),
- })
- );
- });
- it('can edit an existing rule', async function () {
- OrganizationStore.onUpdate(organization);
- const rule = UptimeRuleFixture({
- name: 'Existing Rule',
- projectSlug: project.slug,
- url: 'https://existing-url.com',
- owner: ActorFixture(),
- traceSampling: false,
- });
- render(
- <UptimeAlertForm organization={organization} project={project} rule={rule} />,
- {organization}
- );
- await screen.findByText('Configure Request');
- await selectEvent.select(input('Interval'), 'Every 10 minutes');
- await selectEvent.select(input('Environment'), 'dev');
- const timeout = screen.getByRole('slider', {name: 'Timeout'});
- fireEvent.change(timeout, {target: {value: '7500'}});
- await userEvent.clear(input('URL'));
- await userEvent.type(input('URL'), 'http://another-url.com');
- await selectEvent.clearAll(input('Method'));
- await selectEvent.select(input('Method'), 'POST');
- await userEvent.clear(input('Body'));
- await userEvent.type(input('Body'), '{{"different": "value"}');
- await userEvent.type(input('Name of header 1'), 'X-Something');
- await userEvent.type(input('Value of X-Something'), 'Header Value');
- await userEvent.click(screen.getByRole('button', {name: 'Add Header'}));
- await userEvent.type(input('Name of header 2'), 'X-Another');
- await userEvent.type(input('Value of X-Another'), 'Second Value');
- await userEvent.click(screen.getByRole('checkbox', {name: 'Allow Sampling'}));
- const name = input('Uptime rule name');
- await userEvent.clear(name);
- await userEvent.type(name, 'Updated name');
- await selectEvent.select(input('Owner'), 'Foo Bar');
- const updateMock = MockApiClient.addMockResponse({
- url: `/projects/${organization.slug}/${project.slug}/uptime/${rule.id}/`,
- method: 'PUT',
- });
- await userEvent.click(screen.getByRole('button', {name: 'Save Rule'}));
- expect(updateMock).toHaveBeenCalledWith(
- expect.anything(),
- expect.objectContaining({
- data: expect.objectContaining({
- name: 'Updated name',
- environment: 'dev',
- owner: 'user:1',
- url: 'http://another-url.com',
- method: 'POST',
- headers: [
- ['X-Something', 'Header Value'],
- ['X-Another', 'Second Value'],
- ],
- body: '{"different": "value"}',
- intervalSeconds: 60 * 10,
- traceSampling: true,
- timeoutMs: 7500,
- }),
- })
- );
- });
- it('does not show body for GET and HEAD', async function () {
- OrganizationStore.onUpdate(organization);
- const rule = UptimeRuleFixture({
- projectSlug: project.slug,
- owner: ActorFixture(),
- });
- render(
- <UptimeAlertForm organization={organization} project={project} rule={rule} />,
- {organization}
- );
- await screen.findByText('Configure Request');
- // GET
- await selectEvent.clearAll(input('Method'));
- await selectEvent.select(input('Method'), 'GET');
- expect(screen.queryByRole('textbox', {name: 'Body'})).not.toBeInTheDocument();
- // HEAD
- await selectEvent.clearAll(input('Method'));
- await selectEvent.select(input('Method'), 'HEAD');
- expect(screen.queryByRole('textbox', {name: 'Body'})).not.toBeInTheDocument();
- // POST
- await selectEvent.clearAll(input('Method'));
- await selectEvent.select(input('Method'), 'POST');
- expect(input('Body')).toBeInTheDocument();
- });
- it('updates environments for different projects', async function () {
- OrganizationStore.onUpdate(organization);
- const project1 = ProjectFixture({
- slug: 'project-1',
- environments: ['dev-1', 'prod-1'],
- });
- const project2 = ProjectFixture({
- slug: 'project-2',
- environments: ['dev-2', 'prod-2'],
- });
- ProjectsStore.loadInitialData([project, project1, project2]);
- render(<UptimeAlertForm organization={organization} project={project} />, {
- organization,
- });
- await screen.findByText('Configure Request');
- // Select project 1
- await selectEvent.openMenu(input('Project'));
- expect(screen.getByRole('menuitemradio', {name: 'project-1'})).toBeInTheDocument();
- expect(screen.getByRole('menuitemradio', {name: 'project-2'})).toBeInTheDocument();
- await userEvent.click(screen.getByRole('menuitemradio', {name: 'project-1'}));
- // Verify correct envs
- await selectEvent.openMenu(input('Environment'));
- expect(screen.getByRole('menuitemradio', {name: 'dev-1'})).toBeInTheDocument();
- expect(screen.getByRole('menuitemradio', {name: 'prod-1'})).toBeInTheDocument();
- // Select project 2
- await selectEvent.openMenu(input('Project'));
- await userEvent.click(screen.getByRole('menuitemradio', {name: 'project-2'}));
- // Verify correct envs
- await selectEvent.openMenu(input('Environment'));
- expect(screen.getByRole('menuitemradio', {name: 'dev-2'})).toBeInTheDocument();
- expect(screen.getByRole('menuitemradio', {name: 'prod-2'})).toBeInTheDocument();
- });
- it('can create a new environment', async function () {
- OrganizationStore.onUpdate(organization);
- render(<UptimeAlertForm organization={organization} project={project} />, {
- organization,
- });
- await screen.findByText('Configure Request');
- await userEvent.type(input('Environment'), 'my-custom-env');
- await userEvent.click(
- screen.getByRole('menuitemradio', {name: 'Create "my-custom-env"'})
- );
- await userEvent.clear(input('URL'));
- await userEvent.type(input('URL'), 'http://example.com');
- const name = input('Uptime rule name');
- await userEvent.clear(name);
- await userEvent.type(name, 'New Uptime Rule');
- const updateMock = MockApiClient.addMockResponse({
- url: `/projects/${organization.slug}/${project.slug}/uptime/`,
- method: 'POST',
- });
- await userEvent.click(screen.getByRole('button', {name: 'Create Rule'}));
- expect(updateMock).toHaveBeenCalledWith(
- expect.anything(),
- expect.objectContaining({
- data: expect.objectContaining({}),
- })
- );
- });
- });
|