foreman_remote_execution 18.0.1 → 18.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/foreman_remote_execution/locale/fr/foreman_remote_execution.js +28 -28
  3. data/app/assets/javascripts/foreman_remote_execution/locale/ja/foreman_remote_execution.js +48 -48
  4. data/app/assets/javascripts/foreman_remote_execution/locale/ko/foreman_remote_execution.js +29 -29
  5. data/app/assets/javascripts/foreman_remote_execution/locale/zh_CN/foreman_remote_execution.js +22 -22
  6. data/app/assets/javascripts/foreman_remote_execution/template_invocation.js +0 -170
  7. data/app/assets/stylesheets/foreman_remote_execution/job_invocations.scss +0 -6
  8. data/app/controllers/job_invocations_controller.rb +1 -98
  9. data/app/helpers/job_invocations_helper.rb +0 -49
  10. data/app/helpers/remote_execution_helper.rb +23 -123
  11. data/config/routes.rb +0 -6
  12. data/lib/foreman_remote_execution/plugin.rb +2 -2
  13. data/lib/foreman_remote_execution/version.rb +1 -1
  14. data/locale/foreman_remote_execution.pot +2 -2
  15. data/locale/fr/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  16. data/locale/fr/foreman_remote_execution.po +28 -28
  17. data/locale/ja/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  18. data/locale/ja/foreman_remote_execution.po +48 -48
  19. data/locale/ko/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  20. data/locale/ko/foreman_remote_execution.po +29 -29
  21. data/locale/zh_CN/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  22. data/locale/zh_CN/foreman_remote_execution.po +22 -22
  23. data/package.json +1 -14
  24. data/test/functional/job_invocations_controller_test.rb +0 -77
  25. data/webpack/JobInvocationDetail/JobInvocationConstants.js +5 -0
  26. data/webpack/JobInvocationDetail/JobInvocationToolbarButtons.js +0 -10
  27. data/webpack/JobInvocationDetail/TemplateInvocation.js +12 -69
  28. data/webpack/JobInvocationDetail/TemplateInvocationComponents/OutputCodeBlock.js +94 -72
  29. data/webpack/JobInvocationDetail/TemplateInvocationHelpers.js +33 -0
  30. data/webpack/JobInvocationDetail/__tests__/JobInvocationDetailEmptyState.test.js +1 -1
  31. data/webpack/JobInvocationDetail/__tests__/MainInformation.test.js +0 -6
  32. data/webpack/JobInvocationDetail/__tests__/OutputCodeBlock.test.js +1 -1
  33. data/webpack/JobInvocationDetail/__tests__/TableToolbarActions.test.js +1 -1
  34. data/webpack/JobInvocationDetail/__tests__/TemplateInvocation.test.js +5 -1
  35. data/webpack/JobInvocationDetail/__tests__/TemplateInvocationHelpers.test.js +94 -0
  36. data/webpack/JobInvocationDetail/__tests__/TemplateInvocationPolling.test.js +338 -139
  37. data/webpack/JobInvocationDetail/useTemplateInvocationOutputPolling.js +185 -0
  38. data/webpack/JobWizard/JobWizardPageRerun.js +0 -10
  39. data/webpack/JobWizard/__tests__/autofill.test.js +23 -0
  40. data/webpack/JobWizard/__tests__/integration.test.js +1 -1
  41. data/webpack/JobWizard/autofill.js +18 -4
  42. data/webpack/JobWizard/index.js +1 -18
  43. data/webpack/JobWizard/steps/HostsAndInputs/__tests__/HostsAndInputs.test.js +49 -1
  44. data/webpack/global_index.js +0 -2
  45. data/webpack/index.js +0 -15
  46. data/webpack/react_app/components/RegistrationExtension/__tests__/RexInterface.test.js +1 -1
  47. metadata +6 -53
  48. data/app/assets/stylesheets/foreman_remote_execution/modal_window.scss +0 -4
  49. data/app/helpers/job_invocations_chart_helper.rb +0 -80
  50. data/app/views/job_invocations/_card_results.html.erb +0 -12
  51. data/app/views/job_invocations/_card_schedule.html.erb +0 -33
  52. data/app/views/job_invocations/_card_target_hosts.html.erb +0 -57
  53. data/app/views/job_invocations/_card_user_input.html.erb +0 -16
  54. data/app/views/job_invocations/_description_fields.html.erb +0 -4
  55. data/app/views/job_invocations/_form.html.erb +0 -144
  56. data/app/views/job_invocations/_preview_hosts_modal.html.erb +0 -15
  57. data/app/views/job_invocations/_rerun_taxonomies.html.erb +0 -22
  58. data/app/views/job_invocations/_tab_hosts.html.erb +0 -11
  59. data/app/views/job_invocations/_tab_overview.html.erb +0 -41
  60. data/app/views/job_invocations/_tab_preview_templates.html.erb +0 -20
  61. data/app/views/job_invocations/_user_input.html.erb +0 -21
  62. data/app/views/job_invocations/new.html.erb +0 -9
  63. data/app/views/job_invocations/refresh.js.erb +0 -4
  64. data/app/views/job_invocations/show.html.erb +0 -59
  65. data/app/views/job_invocations/show.js.erb +0 -12
  66. data/app/views/job_invocations/show.json.erb +0 -5
  67. data/test/support/remote_execution_helper.rb +0 -5
  68. data/webpack/react_app/components/TargetingHosts/TargetingHosts.js +0 -62
  69. data/webpack/react_app/components/TargetingHosts/TargetingHostsConsts.js +0 -2
  70. data/webpack/react_app/components/TargetingHosts/TargetingHostsHelpers.js +0 -10
  71. data/webpack/react_app/components/TargetingHosts/TargetingHostsLabelsRow.js +0 -45
  72. data/webpack/react_app/components/TargetingHosts/TargetingHostsLabelsRow.scss +0 -30
  73. data/webpack/react_app/components/TargetingHosts/TargetingHostsPage.js +0 -78
  74. data/webpack/react_app/components/TargetingHosts/TargetingHostsPage.scss +0 -3
  75. data/webpack/react_app/components/TargetingHosts/TargetingHostsSelectors.js +0 -25
  76. data/webpack/react_app/components/TargetingHosts/__tests__/HostItem.test.js +0 -76
  77. data/webpack/react_app/components/TargetingHosts/__tests__/HostStatus.test.js +0 -6
  78. data/webpack/react_app/components/TargetingHosts/__tests__/TargetingHosts.test.js +0 -6
  79. data/webpack/react_app/components/TargetingHosts/__tests__/TargetingHostsPage.test.js +0 -9
  80. data/webpack/react_app/components/TargetingHosts/__tests__/TargetingHostsSelectors.test.js +0 -31
  81. data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/HostStatus.test.js.snap +0 -12
  82. data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHosts.test.js.snap +0 -96
  83. data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsPage.test.js.snap +0 -75
  84. data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsSelectors.test.js.snap +0 -16
  85. data/webpack/react_app/components/TargetingHosts/__tests__/fixtures.js +0 -56
  86. data/webpack/react_app/components/TargetingHosts/components/HostItem.js +0 -39
  87. data/webpack/react_app/components/TargetingHosts/components/HostStatus.js +0 -54
  88. data/webpack/react_app/components/TargetingHosts/index.js +0 -150
  89. data/webpack/react_app/components/jobInvocations/AggregateStatus/index.js +0 -60
  90. data/webpack/react_app/components/jobInvocations/AggregateStatus/index.test.js +0 -76
  91. data/webpack/react_app/components/jobInvocations/index.js +0 -94
  92. data/webpack/react_app/extend/reducers.js +0 -5
  93. data/webpack/react_app/redux/actions/jobInvocations/index.js +0 -87
  94. data/webpack/react_app/redux/consts.js +0 -5
  95. data/webpack/react_app/redux/reducers/index.js +0 -6
  96. data/webpack/react_app/redux/reducers/jobInvocations/index.fixtures.js +0 -46
  97. data/webpack/react_app/redux/reducers/jobInvocations/index.js +0 -34
  98. data/webpack/react_app/redux/reducers/jobInvocations/index.test.js +0 -43
@@ -0,0 +1,33 @@
1
+ export const getLastOutputTimestamp = output =>
2
+ output.length ? output[output.length - 1].timestamp : null;
3
+
4
+ export const normalizeOutput = output =>
5
+ output.reduce((normalized, lineSet) => {
6
+ const previousLineSet = normalized[normalized.length - 1];
7
+
8
+ if (
9
+ previousLineSet &&
10
+ previousLineSet.output_type === lineSet.output_type &&
11
+ !previousLineSet.output.endsWith('\n')
12
+ ) {
13
+ const firstCompleteLine = lineSet.output.match(/^[^\n]*\n/);
14
+ if (firstCompleteLine) {
15
+ normalized[normalized.length - 1] = {
16
+ ...previousLineSet,
17
+ output: previousLineSet.output + firstCompleteLine[0],
18
+ };
19
+
20
+ const remainder = lineSet.output.slice(firstCompleteLine[0].length);
21
+ if (remainder) normalized.push({ ...lineSet, output: remainder });
22
+ return normalized;
23
+ }
24
+ }
25
+
26
+ normalized.push(lineSet);
27
+ return normalized;
28
+ }, []);
29
+
30
+ export const mergeOutput = (currentOutput, newOutput) => {
31
+ if (!newOutput.length) return currentOutput;
32
+ return normalizeOutput([...currentOutput, ...newOutput]);
33
+ };
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { render, screen } from '@testing-library/react';
3
- import '@testing-library/jest-dom/extend-expect';
3
+ import '@testing-library/jest-dom';
4
4
  import { createMemoryHistory } from 'history';
5
5
  import { Router } from 'react-router-dom';
6
6
  import JobInvocationEmptyState from '../JobInvocationEmptyState';
@@ -245,12 +245,6 @@ describe('JobInvocationDetailPage', () => {
245
245
  expect(screen.getByText('Abort')).toBeInTheDocument();
246
246
  expect(screen.queryByText('Enable recurring')).not.toBeInTheDocument();
247
247
  expect(screen.queryByText('Cancel recurring')).not.toBeInTheDocument();
248
- expect(
249
- screen
250
- .getByText('Legacy UI')
251
- .closest('a')
252
- .getAttribute('href')
253
- ).toEqual(`/legacy/job_invocations/${jobId}`);
254
248
  });
255
249
 
256
250
  it('keeps toolbar buttons mounted while job invocation data is refreshing', async () => {
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { render, screen, fireEvent } from '@testing-library/react';
3
- import '@testing-library/jest-dom/extend-expect';
3
+ import '@testing-library/jest-dom';
4
4
  import { OutputCodeBlock } from '../TemplateInvocationComponents/OutputCodeBlock';
5
5
  import { jobInvocationOutput } from './fixtures';
6
6
 
@@ -1,4 +1,4 @@
1
- import '@testing-library/jest-dom/extend-expect';
1
+ import '@testing-library/jest-dom';
2
2
  import { fireEvent, render, screen, waitFor } from '@testing-library/react';
3
3
  import axios from 'axios';
4
4
  import { foremanUrl } from 'foremanReact/common/helpers';
@@ -2,13 +2,16 @@ import React from 'react';
2
2
  import configureMockStore from 'redux-mock-store';
3
3
  import { Provider } from 'react-redux';
4
4
  import { render, screen, fireEvent } from '@testing-library/react';
5
- import '@testing-library/jest-dom/extend-expect';
5
+ import '@testing-library/jest-dom';
6
+ // eslint-disable-next-line import/no-extraneous-dependencies
7
+ import API from 'foremanReact/API';
6
8
  import * as api from 'foremanReact/redux/API';
7
9
  import * as selectors from '../JobInvocationSelectors';
8
10
  import { TemplateInvocation } from '../TemplateInvocation';
9
11
  import { mockTemplateInvocationResponse } from './fixtures';
10
12
 
11
13
  jest.spyOn(api, 'get');
14
+ jest.mock('foremanReact/API');
12
15
  jest.mock('../JobInvocationSelectors');
13
16
 
14
17
  jest.mock('foremanReact/components/ToastsList', () => ({
@@ -47,6 +50,7 @@ const mockProps = {
47
50
 
48
51
  describe('TemplateInvocation', () => {
49
52
  beforeEach(() => {
53
+ API.get.mockImplementation(() => new Promise(() => {}));
50
54
  selectors.selectTemplateInvocationStatus.mockImplementation(() => () =>
51
55
  'RESOLVED'
52
56
  );
@@ -0,0 +1,94 @@
1
+ import {
2
+ getLastOutputTimestamp,
3
+ mergeOutput,
4
+ normalizeOutput,
5
+ } from '../TemplateInvocationHelpers';
6
+
7
+ describe('TemplateInvocationHelpers', () => {
8
+ describe('getLastOutputTimestamp', () => {
9
+ it('returns null for empty output', () => {
10
+ expect(getLastOutputTimestamp([])).toBeNull();
11
+ });
12
+
13
+ it('returns the timestamp of the last output chunk', () => {
14
+ expect(
15
+ getLastOutputTimestamp([
16
+ { timestamp: 1, output: 'first\n' },
17
+ { timestamp: 2, output: 'second\n' },
18
+ ])
19
+ ).toBe(2);
20
+ });
21
+ });
22
+
23
+ describe('normalizeOutput', () => {
24
+ it('joins lines split between chunks in one response', () => {
25
+ const result = normalizeOutput([
26
+ { timestamp: 1, output_type: 'stdout', output: 'hel' },
27
+ {
28
+ timestamp: 2,
29
+ output_type: 'stdout',
30
+ output: 'lo\r\nworld\r\n',
31
+ },
32
+ ]);
33
+
34
+ expect(result).toEqual([
35
+ { timestamp: 1, output_type: 'stdout', output: 'hello\r\n' },
36
+ { timestamp: 2, output_type: 'stdout', output: 'world\r\n' },
37
+ ]);
38
+ });
39
+ });
40
+
41
+ describe('mergeOutput', () => {
42
+ it('appends new chunks without changing existing objects', () => {
43
+ const existingChunk = { timestamp: 1, output: 'first\n' };
44
+ const newChunk = { timestamp: 2, output: 'second\n' };
45
+
46
+ const result = mergeOutput([existingChunk], [newChunk]);
47
+
48
+ expect(result).toEqual([existingChunk, newChunk]);
49
+ expect(result[0]).toBe(existingChunk);
50
+ expect(result[1]).toBe(newChunk);
51
+ });
52
+
53
+ it('joins a line split across two polling responses', () => {
54
+ const result = mergeOutput(
55
+ [{ timestamp: 1, output: 'partial ' }],
56
+ [{ timestamp: 2, output: 'line\nnext line\n' }]
57
+ );
58
+
59
+ expect(result).toEqual([
60
+ { timestamp: 1, output: 'partial line\n' },
61
+ { timestamp: 2, output: 'next line\n' },
62
+ ]);
63
+ });
64
+
65
+ it('does not join chunks of different output types', () => {
66
+ const result = mergeOutput(
67
+ [{ timestamp: 1, output_type: 'stdout', output: 'partial ' }],
68
+ [{ timestamp: 2, output_type: 'stderr', output: 'error\n' }]
69
+ );
70
+
71
+ expect(result).toEqual([
72
+ { timestamp: 1, output_type: 'stdout', output: 'partial ' },
73
+ { timestamp: 2, output_type: 'stderr', output: 'error\n' },
74
+ ]);
75
+ });
76
+
77
+ it('does not append an empty chunk after completing a split line', () => {
78
+ const result = mergeOutput(
79
+ [{ timestamp: 1, output_type: 'stdout', output: 'partial ' }],
80
+ [{ timestamp: 2, output_type: 'stdout', output: 'line\n' }]
81
+ );
82
+
83
+ expect(result).toEqual([
84
+ { timestamp: 1, output_type: 'stdout', output: 'partial line\n' },
85
+ ]);
86
+ });
87
+
88
+ it('returns the current output when there is nothing to append', () => {
89
+ const currentOutput = [{ timestamp: 1, output: 'first\n' }];
90
+
91
+ expect(mergeOutput(currentOutput, [])).toBe(currentOutput);
92
+ });
93
+ });
94
+ });