Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Ingest pipelines] Add ability to stop pipeline simulation #78183

Merged
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,41 @@ const createActions = (testBed: TestBed<TestSubject>) => {
});
},

clickDocumentsDropdown() {
act(() => {
find('documentsDropdown.documentsButton').simulate('click');
});
component.update();
},

clickEditDocumentsButton() {
act(() => {
find('editDocumentsButton').simulate('click');
});
component.update();
},

clickResetButton() {
act(() => {
find('resetButton').simulate('click');
});
component.update();
},

async clickConfirmResetButton() {
const modal = document.body.querySelector(
'[data-test-subj="resetDocumentsConfirmationModal"]'
);
const confirmButton: HTMLButtonElement | null = modal!.querySelector(
'[data-test-subj="confirmModalConfirmButton"]'
);

await act(async () => {
confirmButton!.click();
});
component.update();
},

async clickProcessor(processorSelector: string) {
await act(async () => {
find(`${processorSelector}.manageItemButton`).simulate('click');
Expand Down Expand Up @@ -211,6 +246,7 @@ type TestSubject =
| 'addDocumentsButton'
| 'testPipelineFlyout'
| 'documentsDropdown'
| 'documentsDropdown.documentsButton'
| 'outputTab'
| 'documentsEditor'
| 'runPipelineButton'
Expand All @@ -229,4 +265,6 @@ type TestSubject =
| 'configurationTab'
| 'outputTab'
| 'processorOutputTabContent'
| 'editDocumentsButton'
| 'resetButton'
| string;
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,27 @@ describe('Test pipeline', () => {

const { server, httpRequestsMockHelpers } = setupEnvironment();

// This is a hack
// We need to provide the processor id in the mocked output;
// this is generated dynamically
// As a workaround, the value is added as a data attribute in the UI
// and we retrieve it to generate the mocked output.
const addProcessorTagtoMockOutput = (output: VerboseTestOutput) => {
const { find } = testBed;

const docs = output.docs.map((doc) => {
const results = doc.processor_results.map((result, index) => {
const tag = find(`processors>${index}`).props()['data-processor-id'];
return {
...result,
tag,
};
});
return { processor_results: results };
});
return { docs };
};

beforeAll(() => {
jest.useFakeTimers();
});
Expand Down Expand Up @@ -160,30 +181,76 @@ describe('Test pipeline', () => {
expect(exists('pipelineExecutionError')).toBe(true);
expect(find('pipelineExecutionError').text()).toContain(error.message);
});
});

describe('Processors', () => {
// This is a hack
// We need to provide the processor id in the mocked output;
// this is generated dynamically and not something we can stub.
// As a workaround, the value is added as a data attribute in the UI
// and we retrieve it to generate the mocked output.
const addProcessorTagtoMockOutput = (output: VerboseTestOutput) => {
const { find } = testBed;
describe('Documents dropdown', () => {
beforeEach(async () => {
const { actions } = testBed;

const docs = output.docs.map((doc) => {
const results = doc.processor_results.map((result, index) => {
const tag = find(`processors>${index}`).props()['data-processor-id'];
return {
...result,
tag,
};
});
return { processor_results: results };
httpRequestsMockHelpers.setSimulatePipelineResponse(
addProcessorTagtoMockOutput(SIMULATE_RESPONSE)
);

// Open flyout
actions.clickAddDocumentsButton();
// Add sample documents and click run
actions.addDocumentsJson(JSON.stringify(DOCUMENTS));
await actions.clickRunPipelineButton();
// Close flyout
actions.closeTestPipelineFlyout();
});

it('should open flyout to edit documents', () => {
const { exists, actions } = testBed;

// Dropdown should be visible
expect(exists('documentsDropdown')).toBe(true);

// Open dropdown and edit documents
actions.clickDocumentsDropdown();
actions.clickEditDocumentsButton();

// Flyout should be visible with "Documents" tab enabled
expect(exists('testPipelineFlyout')).toBe(true);
expect(exists('documentsTabContent')).toBe(true);
});

it('should reset documents and stop pipeline simulation', async () => {
const { exists, actions, find } = testBed;

// Dropdown should be visible and processor status should equal "success"
expect(exists('documentsDropdown')).toBe(true);
const initialProcessorStatusLabel = find('processors>0.processorStatusIcon').props()[
'aria-label'
];
expect(initialProcessorStatusLabel).toEqual('Success');

// Open dropdown and click "reset" button
actions.clickDocumentsDropdown();
actions.clickResetButton();

// Verify modal
const modal = document.body.querySelector(
'[data-test-subj="resetDocumentsConfirmationModal"]'
);

expect(modal).not.toBe(null);
expect(modal!.textContent).toContain('Reset');

// Confirm reset and close modal
await actions.clickConfirmResetButton();

// Verify documents and processors were reset
expect(exists('documentsDropdown')).toBe(false);
expect(exists('addDocumentsButton')).toBe(true);
const resetProcessorStatusIconLabel = find('processors>0.processorStatusIcon').props()[
'aria-label'
];
expect(resetProcessorStatusIconLabel).toEqual('Not run');
});
return { docs };
};
});
});

describe('Processors', () => {
it('should show "inactive" processor status by default', async () => {
const { find } = testBed;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import { Document } from '../../../types';

import { TestPipelineFlyoutTab } from '../test_pipeline_flyout_tabs';

import { ResetDocumentsModal } from './reset_documents_modal';

import './documents_dropdown.scss';

const i18nTexts = {
Expand All @@ -31,9 +33,15 @@ const i18nTexts = {
}
),
addDocumentsButtonLabel: i18n.translate(
'xpack.ingestPipelines.pipelineEditor.testPipeline.documentsDropdown.buttonLabel',
'xpack.ingestPipelines.pipelineEditor.testPipeline.documentsDropdown.editDocumentsButtonLabel',
{
defaultMessage: 'Edit documents',
}
),
resetButtonLabel: i18n.translate(
'xpack.ingestPipelines.pipelineEditor.testPipeline.documentsDropdown.resetButtonLabel',
{
defaultMessage: 'Add documents',
defaultMessage: 'Reset',
}
),
popoverTitle: i18n.translate(
Expand All @@ -49,15 +57,18 @@ interface Props {
selectedDocumentIndex: number;
updateSelectedDocument: (index: number) => void;
openFlyout: (activeFlyoutTab: TestPipelineFlyoutTab) => void;
stopPipelineSimulation: () => void;
}

export const DocumentsDropdown: FunctionComponent<Props> = ({
documents,
selectedDocumentIndex,
updateSelectedDocument,
stopPipelineSimulation,
openFlyout,
}) => {
const [showPopover, setShowPopover] = useState<boolean>(false);
const [showResetModal, setShowResetModal] = useState<boolean>(false);

const managePipelineButton = (
<EuiButtonEmpty
Expand All @@ -76,63 +87,97 @@ export const DocumentsDropdown: FunctionComponent<Props> = ({
);

return (
<EuiPopover
isOpen={showPopover}
closePopover={() => setShowPopover(false)}
button={managePipelineButton}
panelPaddingSize="none"
withTitle
repositionOnScroll
data-test-subj="documentsDropdown"
panelClassName="documentsDropdownPanel"
>
<EuiSelectable
singleSelection
options={documents.map((doc, index) => ({
key: index.toString(),
checked: selectedDocumentIndex === index ? 'on' : undefined,
label: i18n.translate('xpack.ingestPipelines.pipelineEditor.testPipeline.documentLabel', {
defaultMessage: 'Document {documentNumber}',
values: {
documentNumber: index + 1,
},
}),
}))}
onChange={(newOptions) => {
const selectedOption = newOptions.find((option) => option.checked === 'on');
if (selectedOption) {
updateSelectedDocument(Number(selectedOption.key!));
}

setShowPopover(false);
}}
<>
<EuiPopover
isOpen={showPopover}
closePopover={() => setShowPopover(false)}
button={managePipelineButton}
panelPaddingSize="none"
withTitle
repositionOnScroll
data-test-subj="documentsDropdown"
panelClassName="documentsDropdownPanel"
>
{(list, search) => (
<div>
<EuiPopoverTitle>{i18nTexts.popoverTitle}</EuiPopoverTitle>
{list}
</div>
)}
</EuiSelectable>

<EuiHorizontalRule margin="xs" />

<EuiFlexGroup justifyContent="center">
<EuiFlexItem grow={false}>
<EuiButton
size="s"
onClick={() => {
openFlyout('documents');
setShowPopover(false);
}}
data-test-subj="addDocumentsButton"
>
{i18nTexts.addDocumentsButtonLabel}
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>

<EuiSpacer size="s" />
</EuiPopover>
<EuiSelectable
singleSelection
data-test-subj="documentList"
options={documents.map((doc, index) => ({
key: index.toString(),
'data-test-subj': 'documentListItem',
checked: selectedDocumentIndex === index ? 'on' : undefined,
label: i18n.translate(
'xpack.ingestPipelines.pipelineEditor.testPipeline.documentLabel',
{
defaultMessage: 'Document {documentNumber}',
values: {
documentNumber: index + 1,
},
}
),
}))}
onChange={(newOptions) => {
const selectedOption = newOptions.find((option) => option.checked === 'on');
if (selectedOption) {
updateSelectedDocument(Number(selectedOption.key!));
}

setShowPopover(false);
}}
>
{(list) => (
<>
<EuiPopoverTitle>{i18nTexts.popoverTitle}</EuiPopoverTitle>
{list}
</>
)}
</EuiSelectable>

<EuiHorizontalRule margin="xs" />

<EuiSpacer size="s" />

<EuiFlexGroup justifyContent="center" gutterSize="xs">
<EuiFlexItem grow={false}>
<EuiButton
size="s"
onClick={() => {
openFlyout('documents');
setShowPopover(false);
}}
data-test-subj="editDocumentsButton"
>
{i18nTexts.addDocumentsButtonLabel}
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>

<EuiSpacer size="s" />

<EuiFlexGroup justifyContent="center" gutterSize="s">
<EuiFlexItem grow={false}>
<EuiButtonEmpty
size="s"
color="danger"
onClick={() => {
setShowResetModal(true);
setShowPopover(false);
}}
data-test-subj="resetButton"
>
{i18nTexts.resetButtonLabel}
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>

<EuiSpacer size="s" />
</EuiPopover>

{showResetModal && (
<ResetDocumentsModal
stopPipelineSimulation={stopPipelineSimulation}
closeModal={() => setShowResetModal(false)}
/>
)}
</>
);
};
Loading