From 30437ed9148a414cc9203c6c443d347bd9de6c25 Mon Sep 17 00:00:00 2001
From: David Liu <48995019+dliu27@users.noreply.github.com>
Date: Mon, 9 Dec 2024 14:01:55 -0500
Subject: [PATCH] [11/n] [RFC] add jest tests (#26249)
## Summary & Motivation
Linear:
https://linear.app/dagster-labs/issue/FE-713/add-jest-tests-for-sensors-and-schedules
Adds jest tests for launch all
## How I Tested These Changes
yarn jest, ts, lint
---
.../ui-core/src/ticks/DryRunRequestTable.tsx | 9 +-
.../EvaluateScheduleDialog.fixtures.tsx | 91 +++++++++-
.../SensorDryRunDialog.fixtures.tsx | 158 ++++++++++++++++++
.../__tests__/DryRunRequestTable.test.tsx | 21 ++-
.../__tests__/EvaluateScheduleDialog.test.tsx | 68 ++++++++
.../__tests__/SensorDryRunDialog.test.tsx | 57 ++++++-
.../graphql/test_run_launcher.py | 8 +-
7 files changed, 398 insertions(+), 14 deletions(-)
diff --git a/js_modules/dagster-ui/packages/ui-core/src/ticks/DryRunRequestTable.tsx b/js_modules/dagster-ui/packages/ui-core/src/ticks/DryRunRequestTable.tsx
index ca8082e8561fb..b9859d06be68a 100644
--- a/js_modules/dagster-ui/packages/ui-core/src/ticks/DryRunRequestTable.tsx
+++ b/js_modules/dagster-ui/packages/ui-core/src/ticks/DryRunRequestTable.tsx
@@ -52,6 +52,7 @@ export const RunRequestTable = ({runRequests, isJob, repoAddress, mode, jobName}
{
setSelectedRequest(request);
setVisibleDialog('config');
@@ -91,10 +92,14 @@ export const RunRequestTable = ({runRequests, isJob, repoAddress, mode, jobName}
);
};
-function PreviewButton({onClick}: {onClick: () => void}) {
+function PreviewButton({request, onClick}: {request: RunRequestFragment; onClick: () => void}) {
return (
- } onClick={onClick} />
+ }
+ onClick={onClick}
+ data-testid={testId(`preview-${request.runKey || ''}`)}
+ />
);
}
diff --git a/js_modules/dagster-ui/packages/ui-core/src/ticks/__fixtures__/EvaluateScheduleDialog.fixtures.tsx b/js_modules/dagster-ui/packages/ui-core/src/ticks/__fixtures__/EvaluateScheduleDialog.fixtures.tsx
index a747734e79869..724b62da00125 100644
--- a/js_modules/dagster-ui/packages/ui-core/src/ticks/__fixtures__/EvaluateScheduleDialog.fixtures.tsx
+++ b/js_modules/dagster-ui/packages/ui-core/src/ticks/__fixtures__/EvaluateScheduleDialog.fixtures.tsx
@@ -1,14 +1,21 @@
import {MockedResponse} from '@apollo/client/testing';
import {
+ RunStatus,
buildDryRunInstigationTick,
buildErrorChainLink,
+ buildLaunchMultipleRunsResult,
+ buildLaunchRunSuccess,
+ buildPipelineSnapshot,
buildPipelineTag,
buildPythonError,
+ buildRun,
buildRunRequest,
buildSchedule,
buildTickEvaluation,
} from '../../graphql/types';
+import {LAUNCH_MULTIPLE_RUNS_MUTATION} from '../../runs/RunUtils';
+import {LaunchMultipleRunsMutation} from '../../runs/types/RunUtils.types';
import {GET_SCHEDULE_QUERY, SCHEDULE_DRY_RUN_MUTATION} from '../EvaluateScheduleDialog';
import {GetScheduleQuery, ScheduleDryRunMutation} from '../types/EvaluateScheduleDialog.types';
@@ -63,7 +70,7 @@ export const scheduleDryWithWithRunRequest = {
value: 'okay',
}),
],
- runKey: null,
+ runKey: 'EvaluateScheduleDialog.test.tsx:1675705668.993122345',
}),
],
skipReason: null,
@@ -164,3 +171,85 @@ export const ScheduleDryRunMutationSkipped: MockedResponse = {
+ request: {
+ query: LAUNCH_MULTIPLE_RUNS_MUTATION,
+ variables: {
+ executionParamsList: [
+ {
+ runConfigData: 'ops:\n configurable_op:\n config:\n scheduled_date: 2023-01-29',
+ selector: {
+ jobName: 'saepe',
+ repositoryLocationName: 'testLocation',
+ repositoryName: 'testName',
+ assetSelection: [],
+ assetCheckSelection: [],
+ solidSelection: undefined,
+ },
+ mode: 'default',
+ executionMetadata: {
+ tags: [
+ {
+ key: 'dagster/schedule_name',
+ value: 'configurable_job_schedule',
+ },
+ {
+ key: 'date',
+ value: '2023-01-29',
+ },
+ {
+ key: 'github_test',
+ value: 'test',
+ },
+ {
+ key: 'okay_t2',
+ value: 'okay',
+ },
+ ],
+ },
+ },
+ ],
+ },
+ },
+ result: {
+ data: {
+ __typename: 'Mutation',
+ launchMultipleRuns: buildLaunchMultipleRunsResult({
+ launchMultipleRunsResult: [
+ buildLaunchRunSuccess({
+ run: buildRun({
+ id: '504b3a77-d6c4-440c-a128-7f59c9d75d59',
+ pipeline: buildPipelineSnapshot({
+ name: 'saepe',
+ }),
+ tags: [
+ buildPipelineTag({
+ key: 'dagster/schedule_name',
+ value: 'configurable_job_schedule',
+ }),
+ buildPipelineTag({
+ key: 'date',
+ value: '2023-01-29',
+ }),
+ buildPipelineTag({
+ key: 'github_test',
+ value: 'test',
+ }),
+ buildPipelineTag({
+ key: 'okay_t2',
+ value: 'okay',
+ }),
+ ],
+ status: RunStatus.QUEUED,
+ runConfigYaml:
+ 'ops:\n configurable_op:\n config:\n scheduled_date: 2023-01-29',
+ mode: 'default',
+ resolvedOpSelection: null,
+ }),
+ }),
+ ],
+ }),
+ },
+ },
+};
diff --git a/js_modules/dagster-ui/packages/ui-core/src/ticks/__fixtures__/SensorDryRunDialog.fixtures.tsx b/js_modules/dagster-ui/packages/ui-core/src/ticks/__fixtures__/SensorDryRunDialog.fixtures.tsx
index 4959a6cc6dee3..98e691e5086a9 100644
--- a/js_modules/dagster-ui/packages/ui-core/src/ticks/__fixtures__/SensorDryRunDialog.fixtures.tsx
+++ b/js_modules/dagster-ui/packages/ui-core/src/ticks/__fixtures__/SensorDryRunDialog.fixtures.tsx
@@ -3,16 +3,23 @@ import {MockedResponse} from '@apollo/client/testing';
import {
InstigationStatus,
RunRequest,
+ RunStatus,
buildDryRunInstigationTick,
buildErrorChainLink,
buildInstigationState,
+ buildLaunchMultipleRunsResult,
+ buildLaunchRunSuccess,
+ buildPipelineSnapshot,
buildPipelineTag,
buildPythonError,
+ buildRun,
buildRunRequest,
buildSensor,
buildSensorData,
buildTickEvaluation,
} from '../../graphql/types';
+import {LAUNCH_MULTIPLE_RUNS_MUTATION} from '../../runs/RunUtils';
+import {LaunchMultipleRunsMutation} from '../../runs/types/RunUtils.types';
import {SET_CURSOR_MUTATION} from '../../sensors/EditCursorDialog';
import {SetSensorCursorMutation} from '../../sensors/types/EditCursorDialog.types';
import {EVALUATE_SENSOR_MUTATION} from '../SensorDryRunDialog';
@@ -177,3 +184,154 @@ export const PersistCursorValueMock: MockedResponse = {
},
},
};
+
+export const SensorLaunchAllMutation: MockedResponse = {
+ request: {
+ query: LAUNCH_MULTIPLE_RUNS_MUTATION,
+ variables: {
+ executionParamsList: [
+ {
+ runConfigData:
+ 'solids:\n read_file:\n config:\n directory: /Users/marcosalazar/code/dagster/js_modules/dagster-ui/packages/ui-core/src/ticks/tests\n filename: DryRunRequestTable.test.tsx',
+ selector: {
+ jobName: 'saepe',
+ repositoryLocationName: 'testLocation',
+ repositoryName: 'testName',
+ assetSelection: [],
+ assetCheckSelection: [],
+ solidSelection: undefined,
+ },
+ mode: 'default',
+ executionMetadata: {
+ tags: [
+ {
+ key: 'dagster2',
+ value: 'test',
+ },
+ {
+ key: 'marco2',
+ value: 'salazar2',
+ },
+ ],
+ },
+ },
+ {
+ runConfigData:
+ 'solids:\n read_file:\n config:\n directory: /Users/marcosalazar/code/dagster/js_modules/dagster-ui/packages/ui-core/src/ticks/tests\n filename: DryRunRequestTable.test.tsx',
+ selector: {
+ jobName: 'saepe',
+ repositoryLocationName: 'testLocation',
+ repositoryName: 'testName',
+ assetSelection: [],
+ assetCheckSelection: [],
+ solidSelection: undefined,
+ },
+ mode: 'default',
+ executionMetadata: {
+ tags: [
+ {
+ key: 'dagster3',
+ value: 'test',
+ },
+ {
+ key: 'marco3',
+ value: 'salazar3',
+ },
+ ],
+ },
+ },
+ {
+ runConfigData:
+ 'solids:\n read_file:\n config:\n directory: /Users/marcosalazar/code/dagster/js_modules/dagster-ui/packages/ui-core/src/ticks/tests\n filename: DryRunRequestTable.test.tsx',
+ selector: {
+ jobName: 'saepe',
+ repositoryLocationName: 'testLocation',
+ repositoryName: 'testName',
+ assetSelection: [],
+ assetCheckSelection: [],
+ solidSelection: undefined,
+ },
+ mode: 'default',
+ executionMetadata: {
+ tags: [
+ {
+ key: 'dagster6',
+ value: 'test',
+ },
+ {
+ key: 'marco6',
+ value: 'salazar6',
+ },
+ ],
+ },
+ },
+ ],
+ },
+ },
+ result: {
+ data: {
+ __typename: 'Mutation',
+ launchMultipleRuns: buildLaunchMultipleRunsResult({
+ launchMultipleRunsResult: [
+ buildLaunchRunSuccess({
+ __typename: 'LaunchRunSuccess',
+ run: buildRun({
+ __typename: 'Run',
+ id: '504b3a77-d6c4-440c-a128-7f59c9d75d59',
+ pipeline: buildPipelineSnapshot({
+ name: 'saepe',
+ }),
+ tags: [
+ buildPipelineTag({key: 'dagster2', value: 'test'}),
+ buildPipelineTag({key: 'marco2', value: 'salazar2'}),
+ ],
+ status: RunStatus.QUEUED,
+ runConfigYaml:
+ 'solids:\n read_file:\n config:\n directory: /Users/marcosalazar/code/dagster/js_modules/dagster-ui/packages/ui-core/src/ticks/tests\n filename: DryRunRequestTable.test.tsx\n',
+ mode: 'default',
+ resolvedOpSelection: null,
+ }),
+ }),
+ buildLaunchRunSuccess({
+ __typename: 'LaunchRunSuccess',
+ run: buildRun({
+ __typename: 'Run',
+ id: '6745cd03-3d89-4fd2-a41f-6b9d9ffdc134',
+ pipeline: buildPipelineSnapshot({
+ name: 'saepe',
+ }),
+ tags: [
+ buildPipelineTag({key: 'dagster3', value: 'test'}),
+ buildPipelineTag({key: 'marco3', value: 'salazar3'}),
+ ],
+
+ status: RunStatus.QUEUED,
+ runConfigYaml:
+ 'solids:\n read_file:\n config:\n directory: /Users/marcosalazar/code/dagster/js_modules/dagster-ui/packages/ui-core/src/ticks/tests\n filename: DryRunRequestTable.test.tsx\n',
+ mode: 'default',
+ resolvedOpSelection: null,
+ }),
+ }),
+ buildLaunchRunSuccess({
+ run: buildRun({
+ id: '7ed35f69-42cf-4518-84a4-c97d0551a56b',
+ pipeline: buildPipelineSnapshot({
+ name: 'simple_config_job',
+ }),
+ tags: [
+ buildPipelineTag({key: 'dagster6', value: 'test'}),
+ buildPipelineTag({key: 'marco6', value: 'salazar6'}),
+ ],
+ status: RunStatus.QUEUED,
+ runConfigYaml:
+ 'solids:\n read_file:\n config:\n directory: /Users/marcosalazar/code/dagster/js_modules/dagster-ui/packages/ui-core/src/ticks/tests\n filename: DryRunRequestTable.test.tsx\n',
+
+ mode: 'default',
+ resolvedOpSelection: null,
+ }),
+ }),
+ ],
+ }),
+ },
+ },
+};
diff --git a/js_modules/dagster-ui/packages/ui-core/src/ticks/__tests__/DryRunRequestTable.test.tsx b/js_modules/dagster-ui/packages/ui-core/src/ticks/__tests__/DryRunRequestTable.test.tsx
index f8f2a760ec648..a1985435b0db5 100644
--- a/js_modules/dagster-ui/packages/ui-core/src/ticks/__tests__/DryRunRequestTable.test.tsx
+++ b/js_modules/dagster-ui/packages/ui-core/src/ticks/__tests__/DryRunRequestTable.test.tsx
@@ -1,4 +1,4 @@
-import {render, screen} from '@testing-library/react';
+import {render, screen, waitFor} from '@testing-library/react';
import {BrowserRouter} from 'react-router-dom';
import {RunRequestTable} from '../DryRunRequestTable';
@@ -9,6 +9,10 @@ jest.mock('../../workspace/WorkspaceContext/util', () => ({
useRepository: jest.fn(() => null),
}));
+jest.mock('../../runs/RunConfigDialog', () => ({
+ RunConfigDialog: () => RunConfigDialog ,
+}));
+
function TestComponent() {
return (
@@ -31,10 +35,19 @@ describe('RunRequestTableTest', () => {
render();
runRequests.forEach((req) => {
- req.tags.forEach(({key, value}) => {
- expect(screen.getByText(`${key}: ${value}`)).toBeVisible();
- });
expect(screen.getByTestId(req.runKey!)).toBeVisible();
});
});
+
+ it('renders preview button and opens dialog on click', async () => {
+ render();
+
+ const previewButton = screen.getByTestId(`preview-${runRequests[0]!.runKey || ''}`);
+ expect(previewButton).toBeVisible();
+ previewButton.click();
+
+ await waitFor(() => {
+ expect(screen.getByText(/RunConfigDialog/i)).toBeVisible();
+ });
+ });
});
diff --git a/js_modules/dagster-ui/packages/ui-core/src/ticks/__tests__/EvaluateScheduleDialog.test.tsx b/js_modules/dagster-ui/packages/ui-core/src/ticks/__tests__/EvaluateScheduleDialog.test.tsx
index 34e6a802718a3..6c17208d0628d 100644
--- a/js_modules/dagster-ui/packages/ui-core/src/ticks/__tests__/EvaluateScheduleDialog.test.tsx
+++ b/js_modules/dagster-ui/packages/ui-core/src/ticks/__tests__/EvaluateScheduleDialog.test.tsx
@@ -1,6 +1,7 @@
import {MockedProvider, MockedResponse} from '@apollo/client/testing';
import {render, screen, waitFor} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
+import {MemoryRouter, useHistory} from 'react-router-dom';
import {Resolvers} from '../../apollo-client';
import {EvaluateScheduleDialog} from '../EvaluateScheduleDialog';
@@ -9,6 +10,7 @@ import {
ScheduleDryRunMutationError,
ScheduleDryRunMutationRunRequests,
ScheduleDryRunMutationSkipped,
+ ScheduleLaunchAllMutation,
} from '../__fixtures__/EvaluateScheduleDialog.fixtures';
// This component is unit tested separately so mocking it out
@@ -18,6 +20,12 @@ jest.mock('../DryRunRequestTable', () => {
};
});
+// Mocking useHistory
+jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useHistory: jest.fn(),
+}));
+
const onCloseMock = jest.fn();
function Test({mocks, resolvers}: {mocks?: MockedResponse[]; resolvers?: Resolvers}) {
@@ -84,4 +92,64 @@ describe('EvaluateScheduleTest', () => {
expect(screen.getByText('Skipped')).toBeVisible();
});
});
+
+ it('allows you to test again', async () => {
+ render();
+ const selectButton = await screen.findByTestId('tick-selection');
+ await userEvent.click(selectButton);
+ await waitFor(() => {
+ expect(screen.getByTestId('tick-5')).toBeVisible();
+ });
+ await userEvent.click(screen.getByTestId('tick-5'));
+ await userEvent.click(screen.getByTestId('continue'));
+ await waitFor(() => {
+ expect(screen.getByText('Skipped')).toBeVisible();
+ });
+ await userEvent.click(screen.getByTestId('try-again'));
+ expect(screen.queryByText('Failed')).toBe(null);
+ expect(screen.queryByText('Skipped')).toBe(null);
+ });
+
+ it('launches all runs', async () => {
+ const pushSpy = jest.fn();
+ const createHrefSpy = jest.fn();
+
+ (useHistory as jest.Mock).mockReturnValue({
+ push: pushSpy,
+ createHref: createHrefSpy,
+ });
+
+ render(
+
+
+ ,
+ );
+ const selectButton = await screen.findByTestId('tick-selection');
+ await userEvent.click(selectButton);
+ await waitFor(() => {
+ expect(screen.getByTestId('tick-5')).toBeVisible();
+ });
+ await userEvent.click(screen.getByTestId('tick-5'));
+ await userEvent.click(screen.getByTestId('continue'));
+ await waitFor(() => {
+ expect(screen.getByText(/1\s+run request/i)).toBeVisible();
+ expect(screen.getByTestId('launch-all')).not.toBeDisabled();
+ });
+
+ userEvent.click(screen.getByTestId('launch-all'));
+
+ await waitFor(() => {
+ expect(screen.getByText(/Launching runs/i)).toBeVisible();
+ });
+
+ await waitFor(() => {
+ expect(pushSpy).toHaveBeenCalled();
+ });
+ });
});
diff --git a/js_modules/dagster-ui/packages/ui-core/src/ticks/__tests__/SensorDryRunDialog.test.tsx b/js_modules/dagster-ui/packages/ui-core/src/ticks/__tests__/SensorDryRunDialog.test.tsx
index 4e23fef7c256c..c8307def66a00 100644
--- a/js_modules/dagster-ui/packages/ui-core/src/ticks/__tests__/SensorDryRunDialog.test.tsx
+++ b/js_modules/dagster-ui/packages/ui-core/src/ticks/__tests__/SensorDryRunDialog.test.tsx
@@ -1,6 +1,7 @@
import {MockedProvider, MockedResponse} from '@apollo/client/testing';
import {render, screen, waitFor} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
+import {MemoryRouter, useHistory} from 'react-router-dom';
import {Resolvers} from '../../apollo-client';
import {SensorDryRunDialog} from '../SensorDryRunDialog';
@@ -13,6 +14,12 @@ jest.mock('../DryRunRequestTable', () => {
};
});
+// Mocking useHistory
+jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useHistory: jest.fn(),
+}));
+
const onCloseMock = jest.fn();
function Test({mocks, resolvers}: {mocks?: MockedResponse[]; resolvers?: Resolvers}) {
@@ -57,6 +64,16 @@ describe('SensorDryRunTest', () => {
});
});
+ it('renders skip reason', async () => {
+ render();
+ const cursorInput = await screen.findByTestId('cursor-input');
+ await userEvent.type(cursorInput, 'testing123');
+ await userEvent.click(screen.getByTestId('continue'));
+ await waitFor(() => {
+ expect(screen.getByText('Skipped')).toBeVisible();
+ });
+ });
+
it('allows you to test again', async () => {
render();
const cursorInput = await screen.findByTestId('cursor-input');
@@ -72,13 +89,47 @@ describe('SensorDryRunTest', () => {
expect(screen.getByTestId('cursor-input')).toBeVisible();
});
- it('renders skip reason', async () => {
- render();
+ it('launches all runs', async () => {
+ const pushSpy = jest.fn();
+ const createHrefSpy = jest.fn();
+
+ (useHistory as jest.Mock).mockReturnValue({
+ push: pushSpy,
+ createHref: createHrefSpy,
+ });
+
+ render(
+
+
+ ,
+ );
const cursorInput = await screen.findByTestId('cursor-input');
await userEvent.type(cursorInput, 'testing123');
await userEvent.click(screen.getByTestId('continue'));
await waitFor(() => {
- expect(screen.getByText('Skipped')).toBeVisible();
+ expect(screen.getByText(/3\srun requests/g)).toBeVisible();
+ expect(screen.queryByText('Skipped')).toBe(null);
+ expect(screen.queryByText('Failed')).toBe(null);
+ });
+
+ await waitFor(() => {
+ expect(screen.getByTestId('launch-all')).not.toBeDisabled();
+ });
+
+ userEvent.click(screen.getByTestId('launch-all'));
+
+ await waitFor(() => {
+ expect(screen.getByText(/Launching runs/i)).toBeVisible();
+ });
+
+ await waitFor(() => {
+ expect(pushSpy).toHaveBeenCalled();
});
});
});
diff --git a/python_modules/dagster-graphql/dagster_graphql_tests/graphql/test_run_launcher.py b/python_modules/dagster-graphql/dagster_graphql_tests/graphql/test_run_launcher.py
index 3a58623c301eb..e56f220535e71 100644
--- a/python_modules/dagster-graphql/dagster_graphql_tests/graphql/test_run_launcher.py
+++ b/python_modules/dagster-graphql/dagster_graphql_tests/graphql/test_run_launcher.py
@@ -272,7 +272,7 @@ def test_launch_multiple_runs_success_and_failure(
launchSuccessExecutionParams = [
{
"selector": {
- "repositoryLocationName": "test",
+ "repositoryLocationName": "test_location",
"repositoryName": "test_repo",
"pipelineName": "no_config_job",
"solidSelection": None,
@@ -283,7 +283,7 @@ def test_launch_multiple_runs_success_and_failure(
},
{
"selector": {
- "repositoryLocationName": "test",
+ "repositoryLocationName": "test_location",
"repositoryName": "test_repo",
"pipelineName": "no_config_job",
"solidSelection": None,
@@ -297,7 +297,7 @@ def test_launch_multiple_runs_success_and_failure(
pipelineNotFoundExecutionParams = [
{
"selector": {
- "repositoryLocationName": "test",
+ "repositoryLocationName": "test_location",
"repositoryName": "test_dict_repo",
"pipelineName": "no_config_job",
"solidSelection": None,
@@ -308,7 +308,7 @@ def test_launch_multiple_runs_success_and_failure(
},
{
"selector": {
- "repositoryLocationName": "test",
+ "repositoryLocationName": "test_location",
"repositoryName": "test_dict_repo",
"pipelineName": "no_config_job",
"solidSelection": None,
|