From 3d5f28f79097c3c4b58a45fdb7de40cff432acb9 Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Fri, 4 Dec 2020 12:39:19 -0500 Subject: [PATCH 1/9] Introduce a strict Content-Security-Policy --- Makefile | 2 +- awx/settings/defaults.py | 1 + awx/ui/context_processors.py | 8 ++++++++ awx/ui_next/public/index.html | 2 ++ awx/ui_next/src/index.jsx | 1 + awx/ui_next/src/nonce.js | 5 +++++ installer/roles/kubernetes/templates/configmap.yml.j2 | 2 -- installer/roles/local_docker/templates/nginx.conf.j2 | 2 -- tools/docker-compose/nginx.vh.default.conf | 4 ---- 9 files changed, 18 insertions(+), 9 deletions(-) create mode 100644 awx/ui/context_processors.py create mode 100644 awx/ui_next/src/nonce.js diff --git a/Makefile b/Makefile index 6a7d4af5a5..15aba30dea 100644 --- a/Makefile +++ b/Makefile @@ -474,7 +474,7 @@ ui-release: ui-devel ui-devel: awx/ui_next/node_modules $(NPM_BIN) --prefix awx/ui_next run extract-strings $(NPM_BIN) --prefix awx/ui_next run compile-strings - $(NPM_BIN) --prefix awx/ui_next run build + INLINE_RUNTIME_CHUNK=false $(NPM_BIN) --prefix awx/ui_next run build git checkout awx/ui_next/src/locales mkdir -p awx/public/static/css mkdir -p awx/public/static/js diff --git a/awx/settings/defaults.py b/awx/settings/defaults.py index 6204486456..d47277eaed 100644 --- a/awx/settings/defaults.py +++ b/awx/settings/defaults.py @@ -248,6 +248,7 @@ TEMPLATES = [ 'django.template.context_processors.static', 'django.template.context_processors.tz', 'django.contrib.messages.context_processors.messages', + 'awx.ui.context_processors.csp', 'social_django.context_processors.backends', 'social_django.context_processors.login_redirect', ], diff --git a/awx/ui/context_processors.py b/awx/ui/context_processors.py new file mode 100644 index 0000000000..87c071c285 --- /dev/null +++ b/awx/ui/context_processors.py @@ -0,0 +1,8 @@ +import base64 +import os + + +def csp(request): + return { + 'csp_nonce': base64.encodebytes(os.urandom(32)).decode().rstrip(), + } diff --git a/awx/ui_next/public/index.html b/awx/ui_next/public/index.html index 2d7ff373b7..510accb63e 100644 --- a/awx/ui_next/public/index.html +++ b/awx/ui_next/public/index.html @@ -1,6 +1,7 @@ + @@ -8,6 +9,7 @@ name="description" content="AWX" /> + AWX diff --git a/awx/ui_next/src/index.jsx b/awx/ui_next/src/index.jsx index ad616077ef..a5203370ab 100644 --- a/awx/ui_next/src/index.jsx +++ b/awx/ui_next/src/index.jsx @@ -1,5 +1,6 @@ import React from 'react'; import ReactDOM from 'react-dom'; +import './nonce'; import '@patternfly/react-core/dist/styles/base.css'; import App from './App'; import { BrandName } from './variables'; diff --git a/awx/ui_next/src/nonce.js b/awx/ui_next/src/nonce.js new file mode 100644 index 0000000000..81fed45c6d --- /dev/null +++ b/awx/ui_next/src/nonce.js @@ -0,0 +1,5 @@ +/* global __webpack_nonce__ */ // eslint-disable-line no-unused-vars + +// CSP: Set a special variable to add `nonce` attributes to all styles/script tags +// See https://github.com/webpack/webpack/pull/3210 +__webpack_nonce__ = window.NONCE_ID; // eslint-disable-line no-global-assign, camelcase diff --git a/installer/roles/kubernetes/templates/configmap.yml.j2 b/installer/roles/kubernetes/templates/configmap.yml.j2 index b7553811c1..b239b96783 100644 --- a/installer/roles/kubernetes/templates/configmap.yml.j2 +++ b/installer/roles/kubernetes/templates/configmap.yml.j2 @@ -69,8 +69,6 @@ data: # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months) add_header Strict-Transport-Security max-age=15768000; - add_header Content-Security-Policy "default-src 'self'; connect-src 'self' ws: wss:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' *.pendo.io; img-src 'self' *.pendo.io data:; report-uri /csp-violation/"; - add_header X-Content-Security-Policy "default-src 'self'; connect-src 'self' ws: wss:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' *.pendo.io; img-src 'self' *.pendo.io data:; report-uri /csp-violation/"; # Protect against click-jacking https://www.owasp.org/index.php/Testing_for_Clickjacking_(OTG-CLIENT-009) add_header X-Frame-Options "DENY"; diff --git a/installer/roles/local_docker/templates/nginx.conf.j2 b/installer/roles/local_docker/templates/nginx.conf.j2 index 0c93510bc9..327b59a2fe 100644 --- a/installer/roles/local_docker/templates/nginx.conf.j2 +++ b/installer/roles/local_docker/templates/nginx.conf.j2 @@ -67,8 +67,6 @@ http { # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months) add_header Strict-Transport-Security max-age=15768000; - add_header Content-Security-Policy "default-src 'self'; connect-src 'self' ws: wss:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' *.pendo.io; img-src 'self' *.pendo.io data:; report-uri /csp-violation/"; - add_header X-Content-Security-Policy "default-src 'self'; connect-src 'self' ws: wss:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' *.pendo.io; img-src 'self' *.pendo.io data:; report-uri /csp-violation/"; # Protect against click-jacking https://www.owasp.org/index.php/Testing_for_Clickjacking_(OTG-CLIENT-009) add_header X-Frame-Options "DENY"; diff --git a/tools/docker-compose/nginx.vh.default.conf b/tools/docker-compose/nginx.vh.default.conf index ff7f604b5e..73a4d1cd8d 100644 --- a/tools/docker-compose/nginx.vh.default.conf +++ b/tools/docker-compose/nginx.vh.default.conf @@ -22,8 +22,6 @@ server { # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months) add_header Strict-Transport-Security max-age=15768000; - add_header Content-Security-Policy "default-src 'self'; connect-src 'self' ws: wss:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' *.pendo.io; img-src 'self' *.pendo.io data:; report-uri /csp-violation/"; - add_header X-Content-Security-Policy "default-src 'self'; connect-src 'self' ws: wss:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' *.pendo.io; img-src 'self' *.pendo.io data:; report-uri /csp-violation/"; location /static/ { root /awx_devel; @@ -84,8 +82,6 @@ server { # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months) add_header Strict-Transport-Security max-age=15768000; - add_header Content-Security-Policy "default-src 'self'; connect-src 'self' ws: wss:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' *.pendo.io; img-src 'self' *.pendo.io data:; report-uri /csp-violation/"; - add_header X-Content-Security-Policy "default-src 'self'; connect-src 'self' ws: wss:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' *.pendo.io; img-src 'self' *.pendo.io data:; report-uri /csp-violation/"; location /static/ { root /awx_devel; From 12077627e465d76db4bd5758fe1af8dfb786bc55 Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Mon, 7 Dec 2020 06:28:05 -0500 Subject: [PATCH 2/9] Integrate CSP config with frontend framework --- awx/ui_next/public/index.html | 17 ++++++++++++++--- awx/ui_next/src/index.jsx | 2 +- awx/ui_next/src/nonce.js | 5 ----- awx/ui_next/src/setupCSP.js | 5 +++++ 4 files changed, 20 insertions(+), 9 deletions(-) delete mode 100644 awx/ui_next/src/nonce.js create mode 100644 awx/ui_next/src/setupCSP.js diff --git a/awx/ui_next/public/index.html b/awx/ui_next/public/index.html index 510accb63e..59a709abbf 100644 --- a/awx/ui_next/public/index.html +++ b/awx/ui_next/public/index.html @@ -1,7 +1,15 @@ - + <% if (process.env.NODE_ENV === 'production') { %> + + + <% } %> @@ -9,11 +17,14 @@ name="description" content="AWX" /> - AWX -
+ <% if (process.env.NODE_ENV === 'production') { %> +
+ <% } else { %> +
+ <% } %> diff --git a/awx/ui_next/src/index.jsx b/awx/ui_next/src/index.jsx index a5203370ab..8bbeb9e866 100644 --- a/awx/ui_next/src/index.jsx +++ b/awx/ui_next/src/index.jsx @@ -1,6 +1,6 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import './nonce'; +import './setupCSP'; import '@patternfly/react-core/dist/styles/base.css'; import App from './App'; import { BrandName } from './variables'; diff --git a/awx/ui_next/src/nonce.js b/awx/ui_next/src/nonce.js deleted file mode 100644 index 81fed45c6d..0000000000 --- a/awx/ui_next/src/nonce.js +++ /dev/null @@ -1,5 +0,0 @@ -/* global __webpack_nonce__ */ // eslint-disable-line no-unused-vars - -// CSP: Set a special variable to add `nonce` attributes to all styles/script tags -// See https://github.com/webpack/webpack/pull/3210 -__webpack_nonce__ = window.NONCE_ID; // eslint-disable-line no-global-assign, camelcase diff --git a/awx/ui_next/src/setupCSP.js b/awx/ui_next/src/setupCSP.js new file mode 100644 index 0000000000..ee4e2465ac --- /dev/null +++ b/awx/ui_next/src/setupCSP.js @@ -0,0 +1,5 @@ +/* eslint-disable */ + +// Set a special variable to add `nonce` attributes to all styles/script tags +// See https://github.com/webpack/webpack/pull/3210 +__webpack_nonce__ = window.NONCE_ID; From 548ebd5999d2e91737d41e9ffd79425c7721a352 Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Mon, 7 Dec 2020 07:22:45 -0500 Subject: [PATCH 3/9] Add w3c-compliant reporting for CSP violations --- awx/ui_next/public/index.html | 2 +- awx/ui_next/src/setupCSP.js | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/awx/ui_next/public/index.html b/awx/ui_next/public/index.html index 59a709abbf..280c34a94e 100644 --- a/awx/ui_next/public/index.html +++ b/awx/ui_next/public/index.html @@ -7,7 +7,7 @@ <% } %> diff --git a/awx/ui_next/src/setupCSP.js b/awx/ui_next/src/setupCSP.js index ee4e2465ac..77d40c5775 100644 --- a/awx/ui_next/src/setupCSP.js +++ b/awx/ui_next/src/setupCSP.js @@ -3,3 +3,28 @@ // Set a special variable to add `nonce` attributes to all styles/script tags // See https://github.com/webpack/webpack/pull/3210 __webpack_nonce__ = window.NONCE_ID; + +// Send report when a CSP violation occurs +// See: https://w3c.github.io/webappsec-csp/2/#violation-reports +// See: https://developer.mozilla.org/en-US/docs/Web/API/SecurityPolicyViolationEvent +document.addEventListener('securitypolicyviolation', e => { + const violation = { + 'csp-report': { + 'blocked-uri': e.blockedURI, + 'document-uri': e.documentURI, + 'effective-directive': e.effectiveDirective, + 'original-policy': e.originalPolicy, + referrer: e.referrer, + 'status-code': e.statusCode, + 'violated-directive': e.violatedDirective, + }, + }; + if (e.sourceFile) violation['csp-report']['source-file'] = e.sourceFile; + if (e.lineNumber) violation['csp-report']['line-number'] = e.lineNumber; + if (e.columnNumber) violation['csp-report']['column-number'] = e.columnNumber; + + const xhr = new XMLHttpRequest(); + xhr.open('POST', '/csp-violation/', true); + xhr.setRequestHeader('content-type', 'application/csp-report'); + xhr.send(JSON.stringify(violation)); +}); From ab61675c2d0f304830b7d5c6c1732fcd67230b27 Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Mon, 7 Dec 2020 08:27:53 -0500 Subject: [PATCH 4/9] Add system for strict-csp stdout html generation --- .../src/screens/Job/JobOutput/JobEvent.jsx | 77 +----- .../src/screens/Job/JobOutput/JobOutput.jsx | 236 +++++++++++++----- 2 files changed, 183 insertions(+), 130 deletions(-) diff --git a/awx/ui_next/src/screens/Job/JobOutput/JobEvent.jsx b/awx/ui_next/src/screens/Job/JobOutput/JobEvent.jsx index 59908f695c..29f02e8245 100644 --- a/awx/ui_next/src/screens/Job/JobOutput/JobEvent.jsx +++ b/awx/ui_next/src/screens/Job/JobOutput/JobEvent.jsx @@ -1,6 +1,3 @@ -import Ansi from 'ansi-to-html'; -import hasAnsi from 'has-ansi'; -import { AllHtmlEntities } from 'html-entities'; import React from 'react'; import { JobEventLine, @@ -9,84 +6,18 @@ import { JobEventLineText, } from './shared'; -const EVENT_START_TASK = 'playbook_on_task_start'; -const EVENT_START_PLAY = 'playbook_on_play_start'; -const EVENT_STATS_PLAY = 'playbook_on_stats'; -const TIME_EVENTS = [EVENT_START_TASK, EVENT_START_PLAY, EVENT_STATS_PLAY]; - -const ansi = new Ansi({ - stream: true, - colors: { - 0: '#000', - 1: '#A30000', - 2: '#486B00', - 3: '#795600', - 4: '#00A', - 5: '#A0A', - 6: '#004368', - 7: '#AAA', - 8: '#555', - 9: '#F55', - 10: '#5F5', - 11: '#FF5', - 12: '#55F', - 13: '#F5F', - 14: '#5FF', - 15: '#FFF', - }, -}); -const entities = new AllHtmlEntities(); - -function getTimestamp({ created }) { - const date = new Date(created); - - const dateHours = date.getHours(); - const dateMinutes = date.getMinutes(); - const dateSeconds = date.getSeconds(); - - const stampHours = dateHours < 10 ? `0${dateHours}` : dateHours; - const stampMinutes = dateMinutes < 10 ? `0${dateMinutes}` : dateMinutes; - const stampSeconds = dateSeconds < 10 ? `0${dateSeconds}` : dateSeconds; - - return `${stampHours}:${stampMinutes}:${stampSeconds}`; -} - -function getLineTextHtml({ created, event, start_line, stdout }) { - const sanitized = entities.encode(stdout); - return sanitized.split('\r\n').map((lineText, index) => { - let html; - if (hasAnsi(lineText)) { - html = ansi.toHtml(lineText); - } else { - html = lineText; - } - - if (index === 1 && TIME_EVENTS.includes(event)) { - const time = getTimestamp({ created }); - html += `${time}`; - } - - return { - lineNumber: start_line + index, - html, - }; - }); -} - function JobEvent({ counter, - created, - event, - isClickable, - onJobEventClick, stdout, - start_line, style, type, + lineTextHtml, + isClickable, + onJobEventClick, }) { return !stdout ? null : (
- {getLineTextHtml({ created, event, start_line, stdout }).map( + {lineTextHtml.map( ({ lineNumber, html }) => lineNumber >= 0 && ( { + let html; + if (hasAnsi(lineText)) { + const { cssMap, result } = replaceStyleAttrs(ansi.toHtml(lineText)); + html = result; + lineCssMap = { ...lineCssMap, ...cssMap }; + } else { + html = lineText; + } + + if (index === 1 && TIME_EVENTS.includes(event)) { + const time = getTimestamp({ created }); + html += `${time}`; + } + + lineTextHtml.push({ + lineNumber: start_line + index, + html, + }); + }); + + return { + lineCssMap, + lineTextHtml, + }; +} + const HeaderTitle = styled.div` display: inline-flex; align-items: center; @@ -54,6 +157,8 @@ const OutputWrapper = styled.div` font-size: 15px; height: calc(100vh - 350px); outline: 1px solid #d7d7d7; + ${({ cssMap }) => + Object.keys(cssMap).map(className => `.${className}{${cssMap[className]}}`)} `; const OutputFooter = styled.div` @@ -122,6 +227,7 @@ class JobOutput extends Component { remoteRowCount: 0, isHostModalOpen: false, hostEvent: {}, + cssMap: {}, }; this.cache = new CellMeasurerCache({ @@ -164,7 +270,7 @@ class JobOutput extends Component { componentDidUpdate(prevProps, prevState) { // recompute row heights for any job events that have transitioned // from loading to loaded - const { currentlyLoading } = this.state; + const { currentlyLoading, cssMap } = this.state; let shouldRecomputeRowHeights = false; prevState.currentlyLoading .filter(n => !currentlyLoading.includes(n)) @@ -172,6 +278,9 @@ class JobOutput extends Component { shouldRecomputeRowHeights = true; this.cache.clear(n); }); + if (Object.keys(cssMap).length !== Object.keys(prevState.cssMap).length) { + shouldRecomputeRowHeights = true; + } if (shouldRecomputeRowHeights) { if (this.listRef.recomputeRowHeights) { this.listRef.recomputeRowHeights(); @@ -300,6 +409,13 @@ class JobOutput extends Component { return isHost; }; + let actualLineTextHtml = []; + if (results[index]) { + const { lineTextHtml, lineCssMap } = getLineTextHtml(results[index]); + this.setState(({ cssMap }) => ({ cssMap: { ...cssMap, ...lineCssMap } })); + actualLineTextHtml = lineTextHtml; + } + return ( this.handleHostEventClick(results[index])} className="row" style={style} + lineTextHtml={actualLineTextHtml} {...results[index]} /> ) : ( @@ -389,7 +506,9 @@ class JobOutput extends Component { handleResize({ width }) { if (width !== this._previousWidth) { this.cache.clearAll(); - this.listRef.recomputeRowHeights(); + if (this.listRef) { + this.listRef.recomputeRowHeights(); + } } this._previousWidth = width; } @@ -404,6 +523,7 @@ class JobOutput extends Component { hostEvent, isHostModalOpen, remoteRowCount, + cssMap, } = this.state; if (hasContentLoading) { @@ -415,60 +535,62 @@ class JobOutput extends Component { } return ( - - {isHostModalOpen && ( - + + {isHostModalOpen && ( + + )} + + + +

{job.name}

+
+ +
+ + - )} - - - -

{job.name}

-
- -
- - - - - {({ onRowsRendered, registerChild }) => ( - - {({ width, height }) => { - return ( - { - this.listRef = ref; - registerChild(ref); - }} - deferredMeasurementCache={this.cache} - height={height || 1} - onRowsRendered={onRowsRendered} - rowCount={remoteRowCount} - rowHeight={this.cache.rowHeight} - rowRenderer={this.rowRenderer} - scrollToAlignment="start" - width={width || 1} - overscanRowCount={20} - /> - ); - }} - - )} - - - + + + {({ onRowsRendered, registerChild }) => ( + + {({ width, height }) => { + return ( + { + this.listRef = ref; + registerChild(ref); + }} + deferredMeasurementCache={this.cache} + height={height || 1} + onRowsRendered={onRowsRendered} + rowCount={remoteRowCount} + rowHeight={this.cache.rowHeight} + rowRenderer={this.rowRenderer} + scrollToAlignment="start" + width={width || 1} + overscanRowCount={20} + /> + ); + }} + + )} + + + +
{deletionError && ( )} -
+ ); } } From c120b731a4e586db8c2df293dab21b3edc879521 Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Tue, 8 Dec 2020 11:47:29 -0500 Subject: [PATCH 5/9] Add global mock for webpack csp var --- awx/ui_next/src/setupTests.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/awx/ui_next/src/setupTests.js b/awx/ui_next/src/setupTests.js index 7d59ff1a4c..5518d62c93 100644 --- a/awx/ui_next/src/setupTests.js +++ b/awx/ui_next/src/setupTests.js @@ -19,3 +19,8 @@ global.console = { ...console, debug: jest.fn(), }; + +// This global variable is part of our Content Security Policy framework +// and so this mock ensures that we don't encounter a reference error +// when running the tests +global.__webpack_nonce__ = null; From b3266f6c62cd34d38fb8d50190ac2af074f4016c Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Tue, 8 Dec 2020 11:52:29 -0500 Subject: [PATCH 6/9] Avoid prop reference error in test --- awx/ui_next/src/screens/Job/JobOutput/JobOutput.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/ui_next/src/screens/Job/JobOutput/JobOutput.jsx b/awx/ui_next/src/screens/Job/JobOutput/JobOutput.jsx index f2a5108ca9..4d78d3b364 100644 --- a/awx/ui_next/src/screens/Job/JobOutput/JobOutput.jsx +++ b/awx/ui_next/src/screens/Job/JobOutput/JobOutput.jsx @@ -506,7 +506,7 @@ class JobOutput extends Component { handleResize({ width }) { if (width !== this._previousWidth) { this.cache.clearAll(); - if (this.listRef) { + if (this.listRef?.recomputeRowHeights) { this.listRef.recomputeRowHeights(); } } From 15704e55e16dd9ae685531d35bdcbb290fb2e2bb Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Tue, 8 Dec 2020 12:42:00 -0500 Subject: [PATCH 7/9] Move INLINE_RUNTIME_CHUNK flag to scripts definition We _always_ want INLINE_RUNTIME_CHUNK to be false when building the ui, even if someone happens to unexpectedly make a production build without using the top-level make targets for some reason. --- Makefile | 2 +- awx/ui_next/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 15aba30dea..6a7d4af5a5 100644 --- a/Makefile +++ b/Makefile @@ -474,7 +474,7 @@ ui-release: ui-devel ui-devel: awx/ui_next/node_modules $(NPM_BIN) --prefix awx/ui_next run extract-strings $(NPM_BIN) --prefix awx/ui_next run compile-strings - INLINE_RUNTIME_CHUNK=false $(NPM_BIN) --prefix awx/ui_next run build + $(NPM_BIN) --prefix awx/ui_next run build git checkout awx/ui_next/src/locales mkdir -p awx/public/static/css mkdir -p awx/public/static/js diff --git a/awx/ui_next/package.json b/awx/ui_next/package.json index 252319a713..d223b47d29 100644 --- a/awx/ui_next/package.json +++ b/awx/ui_next/package.json @@ -53,7 +53,7 @@ }, "scripts": { "start": "PORT=3001 HTTPS=true DANGEROUSLY_DISABLE_HOST_CHECK=true react-scripts start", - "build": "react-scripts build", + "build": "INLINE_RUNTIME_CHUNK=false react-scripts build", "test": "TZ='UTC' react-scripts test --coverage --watchAll=false", "test-watch": "TZ='UTC' react-scripts test", "eject": "react-scripts eject", From 51b18aa012e54a14a32606cd1dfb80da89d432f2 Mon Sep 17 00:00:00 2001 From: nixocio Date: Tue, 8 Dec 2020 13:43:24 -0500 Subject: [PATCH 8/9] Fix JobEvent tests Fix JobEvent tests --- .../screens/Job/JobOutput/JobEvent.test.jsx | 62 ++++++++++++++++--- 1 file changed, 52 insertions(+), 10 deletions(-) diff --git a/awx/ui_next/src/screens/Job/JobOutput/JobEvent.test.jsx b/awx/ui_next/src/screens/Job/JobOutput/JobEvent.test.jsx index baceb1c6f4..e76ae0a3ac 100644 --- a/awx/ui_next/src/screens/Job/JobOutput/JobEvent.test.jsx +++ b/awx/ui_next/src/screens/Job/JobOutput/JobEvent.test.jsx @@ -1,6 +1,5 @@ import React from 'react'; import { mountWithContexts } from '../../../../testUtils/enzymeHelpers'; - import JobEvent from './JobEvent'; const mockOnPlayStartEvent = { @@ -24,23 +23,64 @@ const selectors = { lineText: 'JobEventLineText', }; +const singleDigitTimestampEvent = { + ...mockOnPlayStartEvent, + created: '2019-07-11T08:01:02.906001Z', +}; + +const mockSingleDigitTimestampEventLineTextHtml = [ + { lineNumber: 0, html: '' }, + { + lineNumber: 1, + html: + 'PLAY [add hosts to inventory] **************************************************08:01:02', + }, +]; + +const mockAnsiLineTextHtml = [ + { + lineNumber: 4, + html: 'ok: [localhost]', + }, +]; + +const mockOnPlayStartLineTextHtml = [ + { lineNumber: 0, html: '' }, + { + lineNumber: 1, + html: + 'PLAY [add hosts to inventory] **************************************************18:11:22', + }, +]; + describe('', () => { test('initially renders successfully', () => { - mountWithContexts(); + mountWithContexts( + + ); }); test('playbook event timestamps are rendered', () => { - let wrapper = mountWithContexts(); + let wrapper = mountWithContexts( + + ); let lineText = wrapper.find(selectors.lineText); expect( lineText.filterWhere(e => e.text().includes('18:11:22')) ).toHaveLength(1); - const singleDigitTimestampEvent = { - ...mockOnPlayStartEvent, - created: '2019-07-11T08:01:02.906001Z', - }; - wrapper = mountWithContexts(); + wrapper = mountWithContexts( + + ); lineText = wrapper.find(selectors.lineText); expect( lineText.filterWhere(e => e.text().includes('08:01:02')) @@ -48,12 +88,14 @@ describe('', () => { }); test('ansi stdout colors are rendered as html', () => { - const wrapper = mountWithContexts(); + const wrapper = mountWithContexts( + + ); const lineText = wrapper.find(selectors.lineText); expect( lineText .html() - .includes('ok: [localhost]') + .includes('ok: [localhost]') ).toBe(true); }); From ca2f67e0a9c8fa151c0c57f6bc928c28a0a65efe Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Thu, 10 Dec 2020 08:52:50 -0500 Subject: [PATCH 9/9] FFox ESR 78 Compatibility --- awx/ui_next/public/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/ui_next/public/index.html b/awx/ui_next/public/index.html index 280c34a94e..dc5174aa7c 100644 --- a/awx/ui_next/public/index.html +++ b/awx/ui_next/public/index.html @@ -7,7 +7,7 @@ <% } %>