Date: Tue, 12 Oct 2021 16:27:03 +0200
Subject: [PATCH 053/120] Prevent Site Scan container re-rendering on status
change
---
assets/src/settings-page/site-scan.js | 305 +++++++++++++-------------
1 file changed, 148 insertions(+), 157 deletions(-)
diff --git a/assets/src/settings-page/site-scan.js b/assets/src/settings-page/site-scan.js
index 2d0e07a3526..11895cab3b9 100644
--- a/assets/src/settings-page/site-scan.js
+++ b/assets/src/settings-page/site-scan.js
@@ -38,17 +38,12 @@ import {
export function SiteScan() {
const {
cancelSiteScan,
- currentlyScannedUrlIndex,
- isBusy,
isCancelled,
isComplete,
isInitializing,
isReady,
- pluginIssues,
- scannableUrls,
stale,
startSiteScan,
- themeIssues,
} = useContext( SiteScanContext );
const { originalOptions } = useContext( Options );
const {
@@ -56,180 +51,72 @@ export function SiteScan() {
paired_url_structure: pairedUrlStructure,
theme_support: themeSupport,
} = originalOptions;
+ const previewPermalink = STANDARD === themeSupport ? HOME_URL : pairedUrlExamples[ pairedUrlStructure ][ 0 ];
/**
- * Cancel scan on component unmount.
+ * Cancel scan when component unmounts.
*/
useEffect( () => () => cancelSiteScan(), [ cancelSiteScan ] );
/**
- * Show scan summary with a delay so that the progress bar has a chance to
- * complete.
+ * Delay the `isComplete` state so that the progress bar stays at 100% for a
+ * brief moment.
*/
- const [ showSummary, setShowSummary ] = useState( true );
+ const [ isDelayedComplete, setIsDelayedComplete ] = useState( isComplete );
useEffect( () => {
- let timeout;
+ let cleanup = () => {};
- if ( ( isReady || isComplete ) && ! showSummary ) {
- timeout = setTimeout( () => setShowSummary( true ), 500 );
+ if ( isComplete && ! isDelayedComplete ) {
+ cleanup = setTimeout( () => setIsDelayedComplete( true ), 500 );
+ } else if ( ! isComplete && isDelayedComplete ) {
+ setIsDelayedComplete( false );
}
- return () => {
- if ( timeout ) {
- clearTimeout( timeout );
- }
- };
- }, [ isComplete, isReady, showSummary ] );
+ return cleanup;
+ }, [ isComplete, isDelayedComplete ] );
- useEffect( () => {
- if ( showSummary && isBusy ) {
- setShowSummary( false );
- }
- }, [ isBusy, showSummary ] );
+ /**
+ * Determine footer content.
+ */
+ let footerContent = null;
- if ( isInitializing ) {
- return (
-
-
-
+ if ( isCancelled || ( stale && ( isReady || isDelayedComplete ) ) ) {
+ footerContent = (
+ startSiteScan( { cache: true } ) }
+ isPrimary={ true }
+ >
+ { __( 'Rescan Site', 'amp' ) }
+
+ );
+ } else if ( ! stale && isDelayedComplete ) {
+ footerContent = (
+
+ { __( 'Browse Site', 'amp' ) }
+
);
}
- if ( isCancelled ) {
- return (
- startSiteScan( { cache: true } ) }
- isPrimary={ true }
- >
- { __( 'Rescan Site', 'amp' ) }
-
- ) }
- >
+ return (
+
+ { __( 'Stale results', 'amp' ) }
+
+ ) : null }
+ footerContent={ footerContent }
+ >
+ { isInitializing && }
+ { isCancelled && (
{ __( 'Site scan has been cancelled. Try again.', 'amp' ) }
-
- );
- }
-
- if ( showSummary ) {
- const hasSiteIssues = themeIssues.length > 0 || pluginIssues.length > 0;
- const previewPermalink = STANDARD === themeSupport ? HOME_URL : pairedUrlExamples[ pairedUrlStructure ][ 0 ];
- const footerContent = isComplete && ! stale
- ? (
-
- { __( 'Browse Site', 'amp' ) }
-
- )
- : (
- startSiteScan( { cache: true } ) }
- isPrimary={ true }
- >
- { __( 'Rescan Site', 'amp' ) }
-
- );
-
- const getMessage = () => {
- if ( isReady ) {
- return (
-
-
- { stale
- ? __( 'Stale results. Rescan your site to ensure everything is working properly.', 'amp' )
- : __( 'No changes since your last scan. Browse your site to ensure everything is working as expected.', 'amp' )
- }
-
-
- );
- }
-
- return (
- <>
- { stale && (
-
-
- { __( 'Stale results. Rescan your site to ensure everything is working properly.', 'amp' ) }
-
-
- ) }
- { hasSiteIssues && (
- template mode recommendations below. Because of plugin issues, you may also want to review and suppress plugins .', 'amp' ),
- '#template-modes',
- '#plugin-suppression',
- ),
- } }
- />
- ) }
- { ! hasSiteIssues && ! stale && (
-
-
- { __( 'Site scan found no issues on your site.', 'amp' ) }
-
-
- ) }
- >
- );
- };
-
- return (
-
- { __( 'Stale results', 'amp' ) }
-
- ) : null }
- footerContent={ footerContent }
- >
- { getMessage() }
- { themeIssues.length > 0 && (
-
- ) }
- { pluginIssues.length > 0 && (
-
- ) }
-
- );
- }
-
- return (
-
-
- { __( 'Site scan is checking if there are AMP compatibility issues with your active theme and plugins. We’ll then recommend how to use the AMP plugin.', 'amp' ) }
-
-
-
- { isComplete
- ? __( 'Scan complete', 'amp' )
- : sprintf(
- // translators: 1: currently scanned URL index; 2: scannable URLs count; 3: scanned page type.
- __( 'Scanning %1$d/%2$d URLs: Checking %3$s…', 'amp' ),
- currentlyScannedUrlIndex + 1,
- scannableUrls.length,
- scannableUrls[ currentlyScannedUrlIndex ]?.label,
- )
- }
-
+ ) }
+ { ( isReady || isDelayedComplete ) ? : }
);
}
@@ -269,3 +156,107 @@ SiteScanDrawer.propTypes = {
children: PropTypes.any,
footerContent: PropTypes.node,
};
+
+/**
+ * Site Scan - in progress state.
+ */
+function SiteScanInProgress() {
+ const {
+ currentlyScannedUrlIndex,
+ isComplete,
+ scannableUrls,
+ } = useContext( SiteScanContext );
+
+ return (
+ <>
+
+ { __( 'Site scan is checking if there are AMP compatibility issues with your active theme and plugins. We’ll then recommend how to use the AMP plugin.', 'amp' ) }
+
+
+
+ { isComplete
+ ? __( 'Scan complete', 'amp' )
+ : sprintf(
+ // translators: 1: currently scanned URL index; 2: scannable URLs count; 3: scanned page type.
+ __( 'Scanning %1$d/%2$d URLs: Checking %3$s…', 'amp' ),
+ currentlyScannedUrlIndex + 1,
+ scannableUrls.length,
+ scannableUrls[ currentlyScannedUrlIndex ]?.label,
+ )
+ }
+
+ >
+ );
+}
+
+/**
+ * Site Scan - summary state.
+ */
+function SiteScanSummary() {
+ const {
+ isReady,
+ pluginIssues,
+ stale,
+ themeIssues,
+ } = useContext( SiteScanContext );
+ const hasSiteIssues = themeIssues.length > 0 || pluginIssues.length > 0;
+
+ return (
+ <>
+ { isReady ? (
+
+
+ { stale
+ ? __( 'Stale results. Rescan your site to ensure everything is working properly.', 'amp' )
+ : __( 'No changes since your last scan. Browse your site to ensure everything is working as expected.', 'amp' )
+ }
+
+
+ ) : (
+ <>
+ { stale && (
+
+
+ { __( 'Stale results. Rescan your site to ensure everything is working properly.', 'amp' ) }
+
+
+ ) }
+ { hasSiteIssues && (
+ template mode recommendations below. Because of plugin issues, you may also want to review and suppress plugins .', 'amp' ),
+ '#template-modes',
+ '#plugin-suppression',
+ ),
+ } }
+ />
+ ) }
+ { ! hasSiteIssues && ! stale && (
+
+
+ { __( 'Site scan found no issues on your site.', 'amp' ) }
+
+
+ ) }
+ >
+ ) }
+ { themeIssues.length > 0 && (
+
+ ) }
+ { pluginIssues.length > 0 && (
+
+ ) }
+ >
+ );
+}
From ee1417156f5d356e912849d4c98859fea7695164 Mon Sep 17 00:00:00 2001
From: Piotr Delawski
Date: Tue, 12 Oct 2021 17:01:54 +0200
Subject: [PATCH 054/120] Unify naming convention; refactor delayed flag into a
custom hook
---
.../site-scan-context-provider/index.js | 26 +++++------
.../pages/site-scan/index.js | 44 +++++++------------
assets/src/settings-page/site-scan.js | 35 +++++----------
assets/src/utils/use-delayed-flag.js | 33 ++++++++++++++
4 files changed, 74 insertions(+), 64 deletions(-)
create mode 100644 assets/src/utils/use-delayed-flag.js
diff --git a/assets/src/components/site-scan-context-provider/index.js b/assets/src/components/site-scan-context-provider/index.js
index 34e571185f7..99423f66256 100644
--- a/assets/src/components/site-scan-context-provider/index.js
+++ b/assets/src/components/site-scan-context-provider/index.js
@@ -1,7 +1,7 @@
/**
* WordPress dependencies
*/
-import { createContext, useCallback, useContext, useEffect, useReducer, useRef } from '@wordpress/element';
+import { createContext, useCallback, useContext, useEffect, useReducer, useRef, useState } from '@wordpress/element';
import apiFetch from '@wordpress/api-fetch';
import { usePrevious } from '@wordpress/compose';
import { addQueryArgs } from '@wordpress/url';
@@ -24,8 +24,8 @@ export const SiteScan = createContext();
const ACTION_SCANNABLE_URLS_REQUEST = 'ACTION_SCANNABLE_URLS_REQUEST';
const ACTION_SCANNABLE_URLS_FETCH = 'ACTION_SCANNABLE_URLS_FETCH';
const ACTION_SCANNABLE_URLS_RECEIVE = 'ACTION_SCANNABLE_URLS_RECEIVE';
-const ACTION_START_SITE_SCAN = 'ACTION_START_SITE_SCAN';
-const ACTION_SCAN_IN_PROGRESS = 'ACTION_SCAN_IN_PROGRESS';
+const ACTION_SCAN_INITIALIZE = 'ACTION_SCAN_INITIALIZE';
+const ACTION_SCAN_VALIDATE_URL = 'ACTION_SCAN_VALIDATE_URL';
const ACTION_SCAN_RECEIVE_ISSUES = 'ACTION_SCAN_RECEIVE_ISSUES';
const ACTION_SCAN_NEXT_URL = 'ACTION_SCAN_NEXT_URL';
const ACTION_SCAN_INVALIDATE = 'ACTION_SCAN_INVALIDATE';
@@ -36,7 +36,7 @@ const STATUS_FETCHING_SCANNABLE_URLS = 'STATUS_FETCHING_SCANNABLE_URLS';
const STATUS_READY = 'STATUS_READY';
const STATUS_IDLE = 'STATUS_IDLE';
const STATUS_IN_PROGRESS = 'STATUS_IN_PROGRESS';
-const STATUS_COMPLETE = 'STATUS_COMPLETE';
+const STATUS_COMPLETED = 'STATUS_COMPLETED';
const STATUS_CANCELLED = 'STATUS_CANCELLED';
function siteScanReducer( state, action ) {
@@ -57,7 +57,7 @@ function siteScanReducer( state, action ) {
if ( ! action?.scannableUrls?.length || action.scannableUrls.length === 0 ) {
return {
...state,
- status: STATUS_COMPLETE,
+ status: STATUS_COMPLETED,
};
}
@@ -73,8 +73,8 @@ function siteScanReducer( state, action ) {
themeIssues: [ ...new Set( [ ...state.themeIssues, ...siteIssues.themeIssues ] ) ],
};
}
- case ACTION_START_SITE_SCAN: {
- if ( ! [ STATUS_READY, STATUS_COMPLETE, STATUS_CANCELLED ].includes( state.status ) ) {
+ case ACTION_SCAN_INITIALIZE: {
+ if ( ! [ STATUS_READY, STATUS_COMPLETED, STATUS_CANCELLED ].includes( state.status ) ) {
return state;
}
@@ -88,7 +88,7 @@ function siteScanReducer( state, action ) {
currentlyScannedUrlIndex: initialState.currentlyScannedUrlIndex,
};
}
- case ACTION_SCAN_IN_PROGRESS: {
+ case ACTION_SCAN_VALIDATE_URL: {
return {
...state,
status: STATUS_IN_PROGRESS,
@@ -115,12 +115,12 @@ function siteScanReducer( state, action ) {
const hasNextUrl = state.currentlyScannedUrlIndex < state.scannableUrls.length - 1;
return {
...state,
- status: hasNextUrl ? STATUS_IDLE : STATUS_COMPLETE,
+ status: hasNextUrl ? STATUS_IDLE : STATUS_COMPLETED,
currentlyScannedUrlIndex: hasNextUrl ? state.currentlyScannedUrlIndex + 1 : state.currentlyScannedUrlIndex,
};
}
case ACTION_SCAN_INVALIDATE: {
- if ( state.status !== STATUS_COMPLETE ) {
+ if ( state.status !== STATUS_COMPLETED ) {
return state;
}
@@ -214,7 +214,7 @@ export function SiteScanContextProvider( {
const startSiteScan = useCallback( ( args = {} ) => {
dispatch( {
- type: ACTION_START_SITE_SCAN,
+ type: ACTION_SCAN_INITIALIZE,
cache: args?.cache,
} );
}, [] );
@@ -276,7 +276,7 @@ export function SiteScanContextProvider( {
return;
}
- dispatch( { type: ACTION_SCAN_IN_PROGRESS } );
+ dispatch( { type: ACTION_SCAN_VALIDATE_URL } );
try {
const urlType = ampFirst || themeSupport === STANDARD ? 'url' : 'amp_url';
@@ -323,7 +323,7 @@ export function SiteScanContextProvider( {
currentlyScannedUrlIndex,
isBusy: [ STATUS_IDLE, STATUS_IN_PROGRESS ].includes( status ),
isCancelled: status === STATUS_CANCELLED,
- isComplete: status === STATUS_COMPLETE,
+ isCompleted: status === STATUS_COMPLETED,
isInitializing: [ STATUS_REQUEST_SCANNABLE_URLS, STATUS_FETCHING_SCANNABLE_URLS ].includes( status ),
isReady: status === STATUS_READY,
stale,
diff --git a/assets/src/onboarding-wizard/pages/site-scan/index.js b/assets/src/onboarding-wizard/pages/site-scan/index.js
index c53290579c7..5e0543ffdca 100644
--- a/assets/src/onboarding-wizard/pages/site-scan/index.js
+++ b/assets/src/onboarding-wizard/pages/site-scan/index.js
@@ -7,7 +7,7 @@ import PropTypes from 'prop-types';
/**
* WordPress dependencies
*/
-import { useContext, useEffect, useMemo, useState } from '@wordpress/element';
+import { useContext, useEffect, useMemo } from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';
/**
@@ -22,6 +22,7 @@ import { Selectable } from '../../../components/selectable';
import { IconLandscapeHillsCogs } from '../../../components/svg/landscape-hills-cogs';
import { ProgressBar } from '../../../components/progress-bar';
import { PluginsWithIssues, ThemesWithIssues } from '../../../components/site-scan-results';
+import useDelayedFlag from '../../../utils/use-delayed-flag';
/**
* Screen for visualizing a site scan.
@@ -29,11 +30,12 @@ import { PluginsWithIssues, ThemesWithIssues } from '../../../components/site-sc
export function SiteScan() {
const { setCanGoForward } = useContext( Navigation );
const {
- isInitializing,
- isReady,
- isComplete,
cancelSiteScan,
currentlyScannedUrlIndex,
+ isCancelled,
+ isCompleted,
+ isInitializing,
+ isReady,
pluginIssues,
scannableUrls,
startSiteScan,
@@ -47,39 +49,25 @@ export function SiteScan() {
useEffect( () => () => cancelSiteScan(), [ cancelSiteScan ] );
useEffect( () => {
- if ( isReady ) {
+ if ( isReady || isCancelled ) {
startSiteScan();
}
- }, [ isReady, startSiteScan ] );
+ }, [ isCancelled, isReady, startSiteScan ] );
/**
* Allow moving forward.
*/
useEffect( () => {
- if ( isComplete ) {
+ if ( isCompleted ) {
setCanGoForward( true );
}
- }, [ isComplete, setCanGoForward ] );
+ }, [ isCompleted, setCanGoForward ] );
/**
- * Show scan summary with a delay so that the progress bar has a chance to
- * complete.
+ * Delay the `isCompleted` flag so that the progress bar stays at 100% for a
+ * brief moment.
*/
- const [ showSummary, setShowSummary ] = useState( isComplete );
-
- useEffect( () => {
- let timeout;
-
- if ( isComplete && ! showSummary ) {
- timeout = setTimeout( () => setShowSummary( true ), 500 );
- }
-
- return () => {
- if ( timeout ) {
- clearTimeout( timeout );
- }
- };
- }, [ showSummary, isComplete ] );
+ const isDelayedCompleted = useDelayedFlag( isCompleted );
if ( isInitializing ) {
return (
@@ -90,7 +78,7 @@ export function SiteScan() {
);
}
- if ( showSummary ) {
+ if ( isDelayedCompleted ) {
return (
{ __( 'Site scan is checking if there are AMP compatibility issues with your active theme and plugins. We’ll then recommend how to use the AMP plugin.', 'amp' ) }
-
- { isComplete
+ { isCompleted
? __( 'Scan complete', 'amp' )
: sprintf(
// translators: 1: currently scanned URL index; 2: scannable URLs count; 3: scanned page type.
diff --git a/assets/src/settings-page/site-scan.js b/assets/src/settings-page/site-scan.js
index 11895cab3b9..7bd74f31ccd 100644
--- a/assets/src/settings-page/site-scan.js
+++ b/assets/src/settings-page/site-scan.js
@@ -9,7 +9,7 @@ import PropTypes from 'prop-types';
*/
import { __, sprintf } from '@wordpress/i18n';
import { Button } from '@wordpress/components';
-import { useContext, useEffect, useState } from '@wordpress/element';
+import { useContext, useEffect } from '@wordpress/element';
/**
* Internal dependencies
@@ -31,6 +31,7 @@ import {
NOTICE_TYPE_PLAIN,
NOTICE_TYPE_SUCCESS,
} from '../components/amp-notice';
+import useDelayedFlag from '../utils/use-delayed-flag';
/**
* Site Scan component on the settings screen.
@@ -39,7 +40,7 @@ export function SiteScan() {
const {
cancelSiteScan,
isCancelled,
- isComplete,
+ isCompleted,
isInitializing,
isReady,
stale,
@@ -59,29 +60,17 @@ export function SiteScan() {
useEffect( () => () => cancelSiteScan(), [ cancelSiteScan ] );
/**
- * Delay the `isComplete` state so that the progress bar stays at 100% for a
+ * Delay the `isCompleted` flag so that the progress bar stays at 100% for a
* brief moment.
*/
- const [ isDelayedComplete, setIsDelayedComplete ] = useState( isComplete );
-
- useEffect( () => {
- let cleanup = () => {};
-
- if ( isComplete && ! isDelayedComplete ) {
- cleanup = setTimeout( () => setIsDelayedComplete( true ), 500 );
- } else if ( ! isComplete && isDelayedComplete ) {
- setIsDelayedComplete( false );
- }
-
- return cleanup;
- }, [ isComplete, isDelayedComplete ] );
+ const isDelayedCompleted = useDelayedFlag( isCompleted );
/**
* Determine footer content.
*/
let footerContent = null;
- if ( isCancelled || ( stale && ( isReady || isDelayedComplete ) ) ) {
+ if ( isCancelled || ( stale && ( isReady || isDelayedCompleted ) ) ) {
footerContent = (
startSiteScan( { cache: true } ) }
@@ -90,7 +79,7 @@ export function SiteScan() {
{ __( 'Rescan Site', 'amp' ) }
);
- } else if ( ! stale && isDelayedComplete ) {
+ } else if ( ! stale && isDelayedCompleted ) {
footerContent = (
{ __( 'Browse Site', 'amp' ) }
@@ -101,7 +90,7 @@ export function SiteScan() {
return (
{ __( 'Stale results', 'amp' ) }
@@ -116,7 +105,7 @@ export function SiteScan() {
) }
- { ( isReady || isDelayedComplete ) ? : }
+ { ( isReady || isDelayedCompleted ) ? : }
);
}
@@ -163,7 +152,7 @@ SiteScanDrawer.propTypes = {
function SiteScanInProgress() {
const {
currentlyScannedUrlIndex,
- isComplete,
+ isCompleted,
scannableUrls,
} = useContext( SiteScanContext );
@@ -172,12 +161,12 @@ function SiteScanInProgress() {
{ __( 'Site scan is checking if there are AMP compatibility issues with your active theme and plugins. We’ll then recommend how to use the AMP plugin.', 'amp' ) }
-
- { isComplete
+ { isCompleted
? __( 'Scan complete', 'amp' )
: sprintf(
// translators: 1: currently scanned URL index; 2: scannable URLs count; 3: scanned page type.
diff --git a/assets/src/utils/use-delayed-flag.js b/assets/src/utils/use-delayed-flag.js
new file mode 100644
index 00000000000..b88f00b4e36
--- /dev/null
+++ b/assets/src/utils/use-delayed-flag.js
@@ -0,0 +1,33 @@
+/**
+ * WordPress dependencies
+ */
+import { useEffect, useState } from '@wordpress/element';
+
+/**
+ * Delay setting a flag value.
+ *
+ * @param {boolean} flag Flag value to be delayed.
+ * @param {Object} args Arguments.
+ * @param {number} args.delay Delay value in ms.
+ */
+export default function useDelayedFlag( flag, { delay = 500 } = {} ) {
+ /**
+ * Delay the `isCompleted` state so that the progress bar stays at 100% for
+ * a brief moment.
+ */
+ const [ delayedFlag, setDelayedFlag ] = useState( flag );
+
+ useEffect( () => {
+ let cleanup = () => {};
+
+ if ( flag && ! delayedFlag ) {
+ cleanup = setTimeout( () => setDelayedFlag( true ), delay );
+ } else if ( ! flag && delayedFlag ) {
+ setDelayedFlag( false );
+ }
+
+ return cleanup;
+ }, [ flag, delayedFlag, delay ] );
+
+ return delayedFlag;
+}
From f1ae8e6ae541540fe1c478e39330b05bac150976 Mon Sep 17 00:00:00 2001
From: Piotr Delawski
Date: Tue, 12 Oct 2021 17:12:52 +0200
Subject: [PATCH 055/120] Remove needless condition
---
assets/src/settings-page/site-scan.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/assets/src/settings-page/site-scan.js b/assets/src/settings-page/site-scan.js
index 7bd74f31ccd..ed3c3a19510 100644
--- a/assets/src/settings-page/site-scan.js
+++ b/assets/src/settings-page/site-scan.js
@@ -89,7 +89,7 @@ export function SiteScan() {
return (
{ __( 'Stale results', 'amp' ) }
From e136b4b0a89f1643fe564fce15569b504be4fb6e Mon Sep 17 00:00:00 2001
From: Piotr Delawski
Date: Tue, 12 Oct 2021 17:15:13 +0200
Subject: [PATCH 056/120] Fix JS lint issue
---
assets/src/components/site-scan-context-provider/index.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/assets/src/components/site-scan-context-provider/index.js b/assets/src/components/site-scan-context-provider/index.js
index 99423f66256..89195d8a649 100644
--- a/assets/src/components/site-scan-context-provider/index.js
+++ b/assets/src/components/site-scan-context-provider/index.js
@@ -1,7 +1,7 @@
/**
* WordPress dependencies
*/
-import { createContext, useCallback, useContext, useEffect, useReducer, useRef, useState } from '@wordpress/element';
+import { createContext, useCallback, useContext, useEffect, useReducer, useRef } from '@wordpress/element';
import apiFetch from '@wordpress/api-fetch';
import { usePrevious } from '@wordpress/compose';
import { addQueryArgs } from '@wordpress/url';
From ceeb40ba3738a518d4faa0924dbb177f7131f891 Mon Sep 17 00:00:00 2001
From: Piotr Delawski
Date: Tue, 12 Oct 2021 18:29:15 +0200
Subject: [PATCH 057/120] Patch holes in scanner UI state logic
---
assets/src/settings-page/site-scan.js | 81 +++++++++++++++++----------
1 file changed, 51 insertions(+), 30 deletions(-)
diff --git a/assets/src/settings-page/site-scan.js b/assets/src/settings-page/site-scan.js
index ed3c3a19510..ed7d74a82b4 100644
--- a/assets/src/settings-page/site-scan.js
+++ b/assets/src/settings-page/site-scan.js
@@ -9,7 +9,7 @@ import PropTypes from 'prop-types';
*/
import { __, sprintf } from '@wordpress/i18n';
import { Button } from '@wordpress/components';
-import { useContext, useEffect } from '@wordpress/element';
+import { useCallback, useContext, useEffect } from '@wordpress/element';
/**
* Internal dependencies
@@ -66,26 +66,55 @@ export function SiteScan() {
const isDelayedCompleted = useDelayedFlag( isCompleted );
/**
- * Determine footer content.
+ * Get footer content.
*/
- let footerContent = null;
-
- if ( isCancelled || ( stale && ( isReady || isDelayedCompleted ) ) ) {
- footerContent = (
- startSiteScan( { cache: true } ) }
- isPrimary={ true }
- >
- { __( 'Rescan Site', 'amp' ) }
-
- );
- } else if ( ! stale && isDelayedCompleted ) {
- footerContent = (
-
- { __( 'Browse Site', 'amp' ) }
-
- );
- }
+ const getFooterContent = useCallback( () => {
+ if ( isCancelled || ( stale && ( isReady || isDelayedCompleted ) ) ) {
+ return (
+ startSiteScan( { cache: true } ) }
+ isPrimary={ true }
+ >
+ { __( 'Rescan Site', 'amp' ) }
+
+ );
+ }
+
+ if ( ! stale && isDelayedCompleted ) {
+ return (
+
+ { __( 'Browse Site', 'amp' ) }
+
+ );
+ }
+
+ return null;
+ }, [ isCancelled, isDelayedCompleted, isReady, previewPermalink, stale, startSiteScan ] );
+
+ /**
+ * Get main content.
+ */
+ const getContent = useCallback( () => {
+ if ( isInitializing ) {
+ return ;
+ }
+
+ if ( isCancelled ) {
+ return (
+
+
+ { __( 'Site scan has been cancelled. Try again.', 'amp' ) }
+
+
+ );
+ }
+
+ if ( isReady || isDelayedCompleted ) {
+ return ;
+ }
+
+ return ;
+ }, [ isCancelled, isDelayedCompleted, isInitializing, isReady ] );
return (
) : null }
- footerContent={ footerContent }
+ footerContent={ getFooterContent() }
>
- { isInitializing && }
- { isCancelled && (
-
-
- { __( 'Site scan has been cancelled. Try again.', 'amp' ) }
-
-
- ) }
- { ( isReady || isDelayedCompleted ) ? : }
+ { getContent() }
);
}
From 8c1350c6fc4a0578d7761a1532b8deac45219711 Mon Sep 17 00:00:00 2001
From: Piotr Delawski
Date: Wed, 13 Oct 2021 11:58:34 +0200
Subject: [PATCH 058/120] Apply suggestions from code review
Co-authored-by: Weston Ruter
---
assets/src/components/site-scan-context-provider/index.js | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/assets/src/components/site-scan-context-provider/index.js b/assets/src/components/site-scan-context-provider/index.js
index 89195d8a649..8e1b967fbae 100644
--- a/assets/src/components/site-scan-context-provider/index.js
+++ b/assets/src/components/site-scan-context-provider/index.js
@@ -287,11 +287,10 @@ export function SiteScanContextProvider( {
cache: cache || undefined,
nonce: validateNonce,
omit_stylesheets: true,
+ cache_bust: Math.random(),
},
};
- const validationResults = await apiFetch( {
- url: addQueryArgs( url, args ),
- } );
+ const validationResults = await fetch( addQueryArgs( url, args ) );
if ( true === hasUnmounted.current ) {
return;
From 25a552a898975de24d15af1782c10b1c36ebde04 Mon Sep 17 00:00:00 2001
From: Piotr Delawski
Date: Wed, 13 Oct 2021 15:21:56 +0200
Subject: [PATCH 059/120] Simplify site scan reducer and add failed scan state
- Store validation errors directly inside the `scannableUrl` array and derive theme and plugin issues, and staleness flag from that array.
- Store HTTP request errors in the state so that it's possible to find out if the entire scan was unsuccessful (every `scannableUrl` has an `error` set).
- Handle `isFailed` status in the UI.
---
.../get-site-issues.js | 6 +-
.../site-scan-context-provider/index.js | 134 ++++++++++--------
.../pages/site-scan/index.js | 23 ++-
assets/src/settings-page/site-scan.js | 17 ++-
4 files changed, 115 insertions(+), 65 deletions(-)
diff --git a/assets/src/components/site-scan-context-provider/get-site-issues.js b/assets/src/components/site-scan-context-provider/get-site-issues.js
index 28964a4d80f..529376e6ad4 100644
--- a/assets/src/components/site-scan-context-provider/get-site-issues.js
+++ b/assets/src/components/site-scan-context-provider/get-site-issues.js
@@ -9,13 +9,11 @@ export function getSiteIssues( validationResults = [] ) {
const themeIssues = new Set();
for ( const result of validationResults ) {
- const sources = result?.error?.sources ?? result?.sources;
-
- if ( ! sources ) {
+ if ( ! result.sources ) {
continue;
}
- for ( const source of sources ) {
+ for ( const source of result.sources ) {
if ( source.type === 'plugin' && source.name !== 'amp' ) {
pluginIssues.add( source.name.match( /(.*?)(?:\.php)?$/ )[ 1 ] );
} else if ( source.type === 'theme' ) {
diff --git a/assets/src/components/site-scan-context-provider/index.js b/assets/src/components/site-scan-context-provider/index.js
index 8e1b967fbae..86e914c542f 100644
--- a/assets/src/components/site-scan-context-provider/index.js
+++ b/assets/src/components/site-scan-context-provider/index.js
@@ -1,7 +1,7 @@
/**
* WordPress dependencies
*/
-import { createContext, useCallback, useContext, useEffect, useReducer, useRef } from '@wordpress/element';
+import { createContext, useCallback, useContext, useEffect, useMemo, useReducer, useRef } from '@wordpress/element';
import apiFetch from '@wordpress/api-fetch';
import { usePrevious } from '@wordpress/compose';
import { addQueryArgs } from '@wordpress/url';
@@ -14,9 +14,9 @@ import PropTypes from 'prop-types';
/**
* Internal dependencies
*/
+import { STANDARD } from '../../common/constants';
import { useAsyncError } from '../../utils/use-async-error';
import { Options } from '../options-context-provider';
-import { STANDARD } from '../../common/constants';
import { getSiteIssues } from './get-site-issues';
export const SiteScan = createContext();
@@ -26,7 +26,7 @@ const ACTION_SCANNABLE_URLS_FETCH = 'ACTION_SCANNABLE_URLS_FETCH';
const ACTION_SCANNABLE_URLS_RECEIVE = 'ACTION_SCANNABLE_URLS_RECEIVE';
const ACTION_SCAN_INITIALIZE = 'ACTION_SCAN_INITIALIZE';
const ACTION_SCAN_VALIDATE_URL = 'ACTION_SCAN_VALIDATE_URL';
-const ACTION_SCAN_RECEIVE_ISSUES = 'ACTION_SCAN_RECEIVE_ISSUES';
+const ACTION_SCAN_RECEIVE_VALIDATION_ERRORS = 'ACTION_SCAN_RECEIVE_VALIDATION_ERRORS';
const ACTION_SCAN_NEXT_URL = 'ACTION_SCAN_NEXT_URL';
const ACTION_SCAN_INVALIDATE = 'ACTION_SCAN_INVALIDATE';
const ACTION_SCAN_CANCEL = 'ACTION_SCAN_CANCEL';
@@ -37,6 +37,7 @@ const STATUS_READY = 'STATUS_READY';
const STATUS_IDLE = 'STATUS_IDLE';
const STATUS_IN_PROGRESS = 'STATUS_IN_PROGRESS';
const STATUS_COMPLETED = 'STATUS_COMPLETED';
+const STATUS_FAILED = 'STATUS_FAILED';
const STATUS_CANCELLED = 'STATUS_CANCELLED';
function siteScanReducer( state, action ) {
@@ -54,37 +55,28 @@ function siteScanReducer( state, action ) {
};
}
case ACTION_SCANNABLE_URLS_RECEIVE: {
- if ( ! action?.scannableUrls?.length || action.scannableUrls.length === 0 ) {
+ if ( action?.scannableUrls?.length > 0 ) {
return {
...state,
- status: STATUS_COMPLETED,
+ status: STATUS_READY,
+ scannableUrls: action.scannableUrls,
};
}
- const validationErrors = action.scannableUrls.reduce( ( acc, data ) => [ ...acc, ...data?.validation_errors ?? [] ], [] );
- const siteIssues = getSiteIssues( validationErrors );
-
return {
...state,
- status: STATUS_READY,
- scannableUrls: action.scannableUrls,
- stale: Boolean( action.scannableUrls.find( ( error ) => error?.stale === true ) ),
- pluginIssues: [ ...new Set( [ ...state.pluginIssues, ...siteIssues.pluginIssues ] ) ],
- themeIssues: [ ...new Set( [ ...state.themeIssues, ...siteIssues.themeIssues ] ) ],
+ status: STATUS_COMPLETED,
};
}
case ACTION_SCAN_INITIALIZE: {
- if ( ! [ STATUS_READY, STATUS_COMPLETED, STATUS_CANCELLED ].includes( state.status ) ) {
+ if ( ! [ STATUS_READY, STATUS_COMPLETED, STATUS_FAILED, STATUS_CANCELLED ].includes( state.status ) ) {
return state;
}
return {
...state,
status: STATUS_IDLE,
- stale: false,
cache: action.cache,
- themeIssues: [],
- pluginIssues: [],
currentlyScannedUrlIndex: initialState.currentlyScannedUrlIndex,
};
}
@@ -94,17 +86,21 @@ function siteScanReducer( state, action ) {
status: STATUS_IN_PROGRESS,
};
}
- case ACTION_SCAN_RECEIVE_ISSUES: {
- if ( ! action?.validationResults?.length || action.validationResults.length === 0 ) {
- return state;
- }
-
- const siteIssues = getSiteIssues( action.validationResults );
-
+ case ACTION_SCAN_RECEIVE_VALIDATION_ERRORS: {
return {
...state,
- pluginIssues: [ ...new Set( [ ...state.pluginIssues, ...siteIssues.pluginIssues ] ) ],
- themeIssues: [ ...new Set( [ ...state.themeIssues, ...siteIssues.themeIssues ] ) ],
+ scannableUrls: [
+ ...state.scannableUrls.slice( 0, action.scannedUrlIndex ),
+ {
+ ...state.scannableUrls[ action.scannedUrlIndex ],
+ stale: false,
+ error: action.error ?? false,
+ revalidated: ! Boolean( action.error ),
+ validated_url_post: action.error ? {} : action.validatedUrlPost,
+ validation_errors: action.error ? [] : action.validationErrors,
+ },
+ ...state.scannableUrls.slice( action.scannedUrlIndex + 1 ),
+ ],
};
}
case ACTION_SCAN_NEXT_URL: {
@@ -112,21 +108,33 @@ function siteScanReducer( state, action ) {
return state;
}
- const hasNextUrl = state.currentlyScannedUrlIndex < state.scannableUrls.length - 1;
+ if ( state.currentlyScannedUrlIndex < state.scannableUrls.length - 1 ) {
+ return {
+ ...state,
+ status: STATUS_IDLE,
+ currentlyScannedUrlIndex: state.currentlyScannedUrlIndex + 1,
+ };
+ }
+
+ const hasFailed = state.scannableUrls.every( ( scannableUrl ) => Boolean( scannableUrl.error ) );
+
return {
...state,
- status: hasNextUrl ? STATUS_IDLE : STATUS_COMPLETED,
- currentlyScannedUrlIndex: hasNextUrl ? state.currentlyScannedUrlIndex + 1 : state.currentlyScannedUrlIndex,
+ status: hasFailed ? STATUS_FAILED : STATUS_COMPLETED,
};
}
case ACTION_SCAN_INVALIDATE: {
- if ( state.status !== STATUS_COMPLETED ) {
+ if ( ! [ STATUS_COMPLETED, STATUS_FAILED ].includes( state.status ) ) {
return state;
}
return {
...state,
- stale: true,
+ scannableUrls: state.scannableUrls.map( ( scannableUrl ) => ( {
+ ...scannableUrl,
+ stale: true,
+ revalidated: false,
+ } ) ),
};
}
case ACTION_SCAN_CANCEL: {
@@ -137,8 +145,6 @@ function siteScanReducer( state, action ) {
return {
...state,
status: STATUS_CANCELLED,
- themeIssues: [],
- pluginIssues: [],
currentlyScannedUrlIndex: initialState.currentlyScannedUrlIndex,
};
}
@@ -149,13 +155,10 @@ function siteScanReducer( state, action ) {
}
const initialState = {
- themeIssues: [],
- pluginIssues: [],
- status: '',
- scannableUrls: [],
- stale: false,
cache: false,
currentlyScannedUrlIndex: 0,
+ scannableUrls: [],
+ status: '',
};
/**
@@ -181,13 +184,24 @@ export function SiteScanContextProvider( {
const {
cache,
currentlyScannedUrlIndex,
- pluginIssues,
scannableUrls,
- stale,
status,
- themeIssues,
} = state;
+ /**
+ * Memoize plugin and theme issues.
+ */
+ const { pluginIssues, themeIssues, stale } = useMemo( () => {
+ const validationErrors = scannableUrls.reduce( ( acc, scannableUrl ) => [ ...acc, ...scannableUrl?.validation_errors ?? [] ], [] );
+ const siteIssues = getSiteIssues( validationErrors );
+
+ return {
+ pluginIssues: siteIssues.pluginIssues,
+ themeIssues: siteIssues.themeIssues,
+ stale: Boolean( scannableUrls.find( ( scannableUrl ) => scannableUrl?.stale === true ) ),
+ };
+ }, [ scannableUrls ] );
+
/**
* Preflight check.
*/
@@ -290,28 +304,35 @@ export function SiteScanContextProvider( {
cache_bust: Math.random(),
},
};
- const validationResults = await fetch( addQueryArgs( url, args ) );
- if ( true === hasUnmounted.current ) {
- return;
- }
+ const response = await fetch( addQueryArgs( url, args ) );
+ const data = await response.json();
- dispatch( {
- type: ACTION_SCAN_RECEIVE_ISSUES,
- validationResults: validationResults.results,
- } );
- } catch ( e ) {
if ( true === hasUnmounted.current ) {
return;
}
- const ignoredErrorCodes = [ 'AMP_NOT_REQUESTED', 'AMP_NOT_AVAILABLE' ];
- if ( ! e.code || ! ignoredErrorCodes.includes( e.code ) ) {
- setAsyncError( e );
+ if ( response.ok ) {
+ dispatch( {
+ type: ACTION_SCAN_RECEIVE_VALIDATION_ERRORS,
+ scannedUrlIndex: currentlyScannedUrlIndex,
+ revalidated: data.revalidated,
+ validatedUrlPost: data.validated_url_post,
+ validationErrors: data.results.map( ( { error } ) => error ),
+ } );
+ } else {
+ dispatch( {
+ type: ACTION_SCAN_RECEIVE_VALIDATION_ERRORS,
+ scannedUrlIndex: currentlyScannedUrlIndex,
+ error: data?.code || true,
+ } );
}
- } finally {
- dispatch( { type: ACTION_SCAN_NEXT_URL } );
+ } catch ( e ) {
+ // Note that this doesn't catch failed HTTP responses.
+ setAsyncError( e );
}
+
+ dispatch( { type: ACTION_SCAN_NEXT_URL } );
} )();
}, [ ampFirst, cache, currentlyScannedUrlIndex, scannableUrls, setAsyncError, status, themeSupport, validateNonce ] );
@@ -323,11 +344,12 @@ export function SiteScanContextProvider( {
isBusy: [ STATUS_IDLE, STATUS_IN_PROGRESS ].includes( status ),
isCancelled: status === STATUS_CANCELLED,
isCompleted: status === STATUS_COMPLETED,
+ isFailed: status === STATUS_FAILED,
isInitializing: [ STATUS_REQUEST_SCANNABLE_URLS, STATUS_FETCHING_SCANNABLE_URLS ].includes( status ),
isReady: status === STATUS_READY,
- stale,
pluginIssues,
scannableUrls,
+ stale,
startSiteScan,
themeIssues,
} }
diff --git a/assets/src/onboarding-wizard/pages/site-scan/index.js b/assets/src/onboarding-wizard/pages/site-scan/index.js
index 5e0543ffdca..c1f65321a17 100644
--- a/assets/src/onboarding-wizard/pages/site-scan/index.js
+++ b/assets/src/onboarding-wizard/pages/site-scan/index.js
@@ -34,6 +34,7 @@ export function SiteScan() {
currentlyScannedUrlIndex,
isCancelled,
isCompleted,
+ isFailed,
isInitializing,
isReady,
pluginIssues,
@@ -58,10 +59,10 @@ export function SiteScan() {
* Allow moving forward.
*/
useEffect( () => {
- if ( isCompleted ) {
+ if ( isCompleted || isFailed ) {
setCanGoForward( true );
}
- }, [ isCompleted, setCanGoForward ] );
+ }, [ isCompleted, isFailed, setCanGoForward ] );
/**
* Delay the `isCompleted` flag so that the progress bar stays at 100% for a
@@ -78,6 +79,24 @@ export function SiteScan() {
);
}
+ if ( isFailed ) {
+ return (
+
+
+ { __( 'Site scan was unsuccessful.', 'amp' ) }
+
+
+ { __( 'You can trigger the site scan again on the AMP Settings page after completing the Wizard.', 'amp' ) }
+
+ >
+ ) }
+ />
+ );
+ }
+
if ( isDelayedCompleted ) {
return (
{
- if ( isCancelled || ( stale && ( isReady || isDelayedCompleted ) ) ) {
+ if ( isCancelled || isFailed || ( stale && ( isReady || isDelayedCompleted ) ) ) {
return (
startSiteScan( { cache: true } ) }
@@ -89,7 +90,7 @@ export function SiteScan() {
}
return null;
- }, [ isCancelled, isDelayedCompleted, isReady, previewPermalink, stale, startSiteScan ] );
+ }, [ isCancelled, isDelayedCompleted, isFailed, isReady, previewPermalink, stale, startSiteScan ] );
/**
* Get main content.
@@ -99,6 +100,16 @@ export function SiteScan() {
return ;
}
+ if ( isFailed ) {
+ return (
+
+
+ { __( 'Site scan failed. Try again.', 'amp' ) }
+
+
+ );
+ }
+
if ( isCancelled ) {
return (
@@ -114,7 +125,7 @@ export function SiteScan() {
}
return ;
- }, [ isCancelled, isDelayedCompleted, isInitializing, isReady ] );
+ }, [ isCancelled, isDelayedCompleted, isFailed, isInitializing, isReady ] );
return (
Date: Wed, 13 Oct 2021 15:23:36 +0200
Subject: [PATCH 060/120] Preload themes and plugins data
---
src/Admin/OnboardingWizardSubmenuPage.php | 2 ++
src/Admin/OptionsMenu.php | 2 ++
2 files changed, 4 insertions(+)
diff --git a/src/Admin/OnboardingWizardSubmenuPage.php b/src/Admin/OnboardingWizardSubmenuPage.php
index 7577b796cb3..9c4f58e37b9 100644
--- a/src/Admin/OnboardingWizardSubmenuPage.php
+++ b/src/Admin/OnboardingWizardSubmenuPage.php
@@ -290,7 +290,9 @@ protected function add_preload_rest_paths() {
'/amp/v1/options',
'/amp/v1/reader-themes',
'/amp/v1/scannable-urls',
+ '/wp/v2/plugins',
'/wp/v2/settings',
+ '/wp/v2/themes',
'/wp/v2/users/me',
];
diff --git a/src/Admin/OptionsMenu.php b/src/Admin/OptionsMenu.php
index 6d6be333298..4ec1bc58e61 100644
--- a/src/Admin/OptionsMenu.php
+++ b/src/Admin/OptionsMenu.php
@@ -326,7 +326,9 @@ protected function add_preload_rest_paths() {
[ 'url', 'amp_url', 'type', 'label', 'validation_errors', 'stale' ],
'/amp/v1/scannable-urls'
),
+ '/wp/v2/plugins',
'/wp/v2/settings',
+ '/wp/v2/themes',
'/wp/v2/users/me',
];
From 4dd1fcb7597fd691bf626d2c28f87b889ec17614 Mon Sep 17 00:00:00 2001
From: Piotr Delawski
Date: Wed, 13 Oct 2021 15:31:12 +0200
Subject: [PATCH 061/120] Fix JS test
---
.../test/get-site-issues.js | 72 ++++++++-----------
1 file changed, 28 insertions(+), 44 deletions(-)
diff --git a/assets/src/components/site-scan-context-provider/test/get-site-issues.js b/assets/src/components/site-scan-context-provider/test/get-site-issues.js
index d22250fcc0c..af91c257a4b 100644
--- a/assets/src/components/site-scan-context-provider/test/get-site-issues.js
+++ b/assets/src/components/site-scan-context-provider/test/get-site-issues.js
@@ -11,64 +11,48 @@ describe( 'getSiteIssues', () => {
it( 'returns plugin and theme issues', () => {
const validationResult = [
{
- error: {
- sources: [
- { type: 'core', name: 'wp-includes' },
- { type: 'plugin', name: 'amp' },
- ],
- },
+ sources: [
+ { type: 'core', name: 'wp-includes' },
+ { type: 'plugin', name: 'amp' },
+ ],
},
{
- error: {
- sources: [
- { type: 'plugin', name: 'gutenberg' },
- ],
- },
+ sources: [
+ { type: 'plugin', name: 'gutenberg' },
+ ],
},
{
- error: {
- sources: [
- { type: 'core', name: 'wp-includes' },
- { type: 'plugin', name: 'jetpack' },
- ],
- },
+ sources: [
+ { type: 'core', name: 'wp-includes' },
+ { type: 'plugin', name: 'jetpack' },
+ ],
},
{
- error: {
- sources: [
- { type: 'plugin', name: 'jetpack' },
- ],
- },
+ sources: [
+ { type: 'plugin', name: 'jetpack' },
+ ],
},
{
- error: {
- sources: [
- { type: 'plugin', name: 'foo-bar.php' },
- ],
- },
+ sources: [
+ { type: 'plugin', name: 'foo-bar.php' },
+ ],
},
{
- error: {
- sources: [
- { type: 'theme', name: 'twentytwenty' },
- { type: 'core', name: 'wp-includes' },
- ],
- },
+ sources: [
+ { type: 'theme', name: 'twentytwenty' },
+ { type: 'core', name: 'wp-includes' },
+ ],
},
{
- error: {
- sources: [
- { type: 'core', name: 'wp-includes' },
- ],
- },
+ sources: [
+ { type: 'core', name: 'wp-includes' },
+ ],
},
{
- error: {
- sources: [
- { type: 'theme', name: 'twentytwenty' },
- { type: 'core', name: 'wp-includes' },
- ],
- },
+ sources: [
+ { type: 'theme', name: 'twentytwenty' },
+ { type: 'core', name: 'wp-includes' },
+ ],
},
];
From 7b51924d14ab062f7b62b7818ed6f833dde5e1d3 Mon Sep 17 00:00:00 2001
From: Piotr Delawski
Date: Wed, 13 Oct 2021 15:52:49 +0200
Subject: [PATCH 062/120] Determine `previewPermalink` in Site Scan context
provider
---
.../site-scan-context-provider/index.js | 12 ++++++++++--
assets/src/onboarding-wizard/index.js | 2 ++
assets/src/settings-page/index.js | 2 ++
assets/src/settings-page/site-review.js | 15 +++------------
assets/src/settings-page/site-scan.js | 12 ++----------
5 files changed, 19 insertions(+), 24 deletions(-)
diff --git a/assets/src/components/site-scan-context-provider/index.js b/assets/src/components/site-scan-context-provider/index.js
index 86e914c542f..66a8e38ba73 100644
--- a/assets/src/components/site-scan-context-provider/index.js
+++ b/assets/src/components/site-scan-context-provider/index.js
@@ -168,6 +168,7 @@ const initialState = {
* @param {boolean} props.ampFirst Whether scanning should be done with Standard mode being forced.
* @param {?any} props.children Component children.
* @param {boolean} props.fetchCachedValidationErrors Whether to fetch cached validation errors on mount.
+ * @param {string} props.homeUrl Site home URL.
* @param {string} props.scannableUrlsRestPath The REST path for interacting with the scannable URL resources.
* @param {string} props.validateNonce The AMP validate nonce.
*/
@@ -175,6 +176,7 @@ export function SiteScanContextProvider( {
ampFirst = false,
children,
fetchCachedValidationErrors = false,
+ homeUrl,
scannableUrlsRestPath,
validateNonce,
} ) {
@@ -187,9 +189,10 @@ export function SiteScanContextProvider( {
scannableUrls,
status,
} = state;
+ const urlType = ampFirst || themeSupport === STANDARD ? 'url' : 'amp_url';
/**
- * Memoize plugin and theme issues.
+ * Memoize properties.
*/
const { pluginIssues, themeIssues, stale } = useMemo( () => {
const validationErrors = scannableUrls.reduce( ( acc, scannableUrl ) => [ ...acc, ...scannableUrl?.validation_errors ?? [] ], [] );
@@ -202,6 +205,10 @@ export function SiteScanContextProvider( {
};
}, [ scannableUrls ] );
+ const previewPermalink = useMemo( () => {
+ return scannableUrls.find( ( { type } ) => type === 'home' )?.[ urlType ] || homeUrl;
+ }, [ homeUrl, scannableUrls, urlType ] );
+
/**
* Preflight check.
*/
@@ -293,7 +300,6 @@ export function SiteScanContextProvider( {
dispatch( { type: ACTION_SCAN_VALIDATE_URL } );
try {
- const urlType = ampFirst || themeSupport === STANDARD ? 'url' : 'amp_url';
const url = scannableUrls[ currentlyScannedUrlIndex ][ urlType ];
const args = {
'amp-first': ampFirst || undefined,
@@ -348,6 +354,7 @@ export function SiteScanContextProvider( {
isInitializing: [ STATUS_REQUEST_SCANNABLE_URLS, STATUS_FETCHING_SCANNABLE_URLS ].includes( status ),
isReady: status === STATUS_READY,
pluginIssues,
+ previewPermalink,
scannableUrls,
stale,
startSiteScan,
@@ -363,6 +370,7 @@ SiteScanContextProvider.propTypes = {
ampFirst: PropTypes.bool,
children: PropTypes.any,
fetchCachedValidationErrors: PropTypes.bool,
+ homeUrl: PropTypes.string,
scannableUrlsRestPath: PropTypes.string,
validateNonce: PropTypes.string,
};
diff --git a/assets/src/onboarding-wizard/index.js b/assets/src/onboarding-wizard/index.js
index ff85ed54142..5eaa799ce38 100644
--- a/assets/src/onboarding-wizard/index.js
+++ b/assets/src/onboarding-wizard/index.js
@@ -12,6 +12,7 @@ import {
APP_ROOT_ID,
CLOSE_LINK,
CURRENT_THEME,
+ HOME_URL,
SETTINGS_LINK,
OPTIONS_REST_PATH,
READER_THEMES_REST_PATH,
@@ -87,6 +88,7 @@ export function Providers( { children } ) {
diff --git a/assets/src/settings-page/index.js b/assets/src/settings-page/index.js
index 5abbffe045c..882a0a8d6a6 100644
--- a/assets/src/settings-page/index.js
+++ b/assets/src/settings-page/index.js
@@ -5,6 +5,7 @@ import PropTypes from 'prop-types';
import {
CURRENT_THEME,
HAS_DEPENDENCY_SUPPORT,
+ HOME_URL,
OPTIONS_REST_PATH,
READER_THEMES_REST_PATH,
SCANNABLE_URLS_REST_PATH,
@@ -97,6 +98,7 @@ function Providers( { children } ) {
diff --git a/assets/src/settings-page/site-review.js b/assets/src/settings-page/site-review.js
index 00c29ea232b..e4ce13fcf39 100644
--- a/assets/src/settings-page/site-review.js
+++ b/assets/src/settings-page/site-review.js
@@ -1,8 +1,3 @@
-/**
- * External dependencies
- */
-import { HOME_URL } from 'amp-settings';
-
/**
* WordPress dependencies
*/
@@ -19,6 +14,7 @@ import { IconLaptopSearch } from '../components/svg/icon-laptop-search';
import { Options } from '../components/options-context-provider';
import { User } from '../components/user-context-provider';
import { READER, STANDARD, TRANSITIONAL } from '../common/constants';
+import { SiteScan as SiteScanContext } from '../components/site-scan-context-provider';
/**
* Review component on the settings screen.
@@ -29,19 +25,14 @@ export function SiteReview() {
saveReviewPanelDismissedForTemplateMode,
savingReviewPanelDismissedForTemplateMode,
} = useContext( User );
+ const { previewPermalink } = useContext( SiteScanContext );
const { originalOptions } = useContext( Options );
- const {
- paired_url_examples: pairedUrlExamples,
- paired_url_structure: pairedUrlStructure,
- theme_support: themeSupport,
- } = originalOptions;
+ const { theme_support: themeSupport } = originalOptions;
if ( savingReviewPanelDismissedForTemplateMode || reviewPanelDismissedForTemplateMode === themeSupport ) {
return null;
}
- const previewPermalink = STANDARD === themeSupport ? HOME_URL : pairedUrlExamples?.[ pairedUrlStructure ]?.[ 0 ] ?? HOME_URL;
-
return (
Date: Wed, 13 Oct 2021 15:55:06 +0200
Subject: [PATCH 063/120] Fix `scannable-urls` preload path in Onboarding
Wizard
---
src/Admin/OnboardingWizardSubmenuPage.php | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/Admin/OnboardingWizardSubmenuPage.php b/src/Admin/OnboardingWizardSubmenuPage.php
index 9c4f58e37b9..563267c2261 100644
--- a/src/Admin/OnboardingWizardSubmenuPage.php
+++ b/src/Admin/OnboardingWizardSubmenuPage.php
@@ -289,7 +289,11 @@ protected function add_preload_rest_paths() {
$paths = [
'/amp/v1/options',
'/amp/v1/reader-themes',
- '/amp/v1/scannable-urls',
+ add_query_arg(
+ '_fields',
+ [ 'url', 'amp_url', 'type', 'label' ],
+ '/amp/v1/scannable-urls'
+ ),
'/wp/v2/plugins',
'/wp/v2/settings',
'/wp/v2/themes',
From 73156df4a0135b559515f93a64335ba028fdb5bb Mon Sep 17 00:00:00 2001
From: Piotr Delawski
Date: Wed, 13 Oct 2021 16:10:53 +0200
Subject: [PATCH 064/120] Do not determine site issues if scan is in progress
---
.../components/site-scan-context-provider/index.js | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/assets/src/components/site-scan-context-provider/index.js b/assets/src/components/site-scan-context-provider/index.js
index 66a8e38ba73..5166192926a 100644
--- a/assets/src/components/site-scan-context-provider/index.js
+++ b/assets/src/components/site-scan-context-provider/index.js
@@ -195,6 +195,15 @@ export function SiteScanContextProvider( {
* Memoize properties.
*/
const { pluginIssues, themeIssues, stale } = useMemo( () => {
+ // Skip if the scan is in progress.
+ if ( ! [ STATUS_READY, STATUS_COMPLETED ].includes( status ) ) {
+ return {
+ pluginIssues: [],
+ themeIssues: [],
+ stale: undefined,
+ };
+ }
+
const validationErrors = scannableUrls.reduce( ( acc, scannableUrl ) => [ ...acc, ...scannableUrl?.validation_errors ?? [] ], [] );
const siteIssues = getSiteIssues( validationErrors );
@@ -203,7 +212,7 @@ export function SiteScanContextProvider( {
themeIssues: siteIssues.themeIssues,
stale: Boolean( scannableUrls.find( ( scannableUrl ) => scannableUrl?.stale === true ) ),
};
- }, [ scannableUrls ] );
+ }, [ scannableUrls, status ] );
const previewPermalink = useMemo( () => {
return scannableUrls.find( ( { type } ) => type === 'home' )?.[ urlType ] || homeUrl;
From 00d34b699c497d6a122e7f1a43e5908af1768ab2 Mon Sep 17 00:00:00 2001
From: Piotr Delawski
Date: Wed, 13 Oct 2021 16:23:21 +0200
Subject: [PATCH 065/120] Focus on Site Scan panel when starting scan
---
assets/src/settings-page/index.js | 6 +++++-
assets/src/settings-page/site-scan.js | 17 ++++++++++++++---
2 files changed, 19 insertions(+), 4 deletions(-)
diff --git a/assets/src/settings-page/index.js b/assets/src/settings-page/index.js
index 882a0a8d6a6..27d9895cf80 100644
--- a/assets/src/settings-page/index.js
+++ b/assets/src/settings-page/index.js
@@ -201,6 +201,10 @@ function Root( { appRoot } ) {
};
}, [ fetchingOptions ] );
+ const focusSiteScanSection = useCallback( () => {
+ setFocusedSection( 'site-scan' );
+ }, [] );
+
if ( false !== fetchingOptions || null === templateModeWasOverridden ) {
return ;
}
@@ -213,7 +217,7 @@ function Root( { appRoot } ) {
) }
-
+
);
}
+SiteScan.propTypes = {
+ onSiteScan: PropTypes.func,
+};
/**
* Site Scan drawer (settings panel).
From 7e6ec7efd19d96d27f7da9b29274ab813d114e30 Mon Sep 17 00:00:00 2001
From: Piotr Delawski
Date: Wed, 13 Oct 2021 16:50:59 +0200
Subject: [PATCH 066/120] Add missing useEffect dependency
---
assets/src/components/site-scan-context-provider/index.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/assets/src/components/site-scan-context-provider/index.js b/assets/src/components/site-scan-context-provider/index.js
index 5166192926a..4c1ff58523e 100644
--- a/assets/src/components/site-scan-context-provider/index.js
+++ b/assets/src/components/site-scan-context-provider/index.js
@@ -349,7 +349,7 @@ export function SiteScanContextProvider( {
dispatch( { type: ACTION_SCAN_NEXT_URL } );
} )();
- }, [ ampFirst, cache, currentlyScannedUrlIndex, scannableUrls, setAsyncError, status, themeSupport, validateNonce ] );
+ }, [ ampFirst, cache, currentlyScannedUrlIndex, scannableUrls, setAsyncError, status, urlType, validateNonce ] );
return (
Date: Thu, 14 Oct 2021 11:51:56 +0200
Subject: [PATCH 067/120] Update E2E tests: increase stepper items count (Site
Scan added)
---
tests/e2e/specs/amp-onboarding/reader-themes.js | 2 +-
tests/e2e/specs/amp-onboarding/template-mode.js | 4 ++--
tests/e2e/specs/amp-onboarding/transitional-recommendation.js | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/tests/e2e/specs/amp-onboarding/reader-themes.js b/tests/e2e/specs/amp-onboarding/reader-themes.js
index a56f1a9291a..c32b96810a2 100644
--- a/tests/e2e/specs/amp-onboarding/reader-themes.js
+++ b/tests/e2e/specs/amp-onboarding/reader-themes.js
@@ -10,7 +10,7 @@ describe( 'Reader themes', () => {
it( 'shows the correct active stepper item', async () => {
const itemCount = await page.$$eval( '.amp-stepper__item', ( els ) => els.length );
- expect( itemCount ).toBe( 5 );
+ expect( itemCount ).toBe( 6 );
await expect( page ).toMatchElement( '.amp-stepper__item--active', { text: 'Theme Selection' } );
} );
diff --git a/tests/e2e/specs/amp-onboarding/template-mode.js b/tests/e2e/specs/amp-onboarding/template-mode.js
index 796d038f41c..3d5ee6556e8 100644
--- a/tests/e2e/specs/amp-onboarding/template-mode.js
+++ b/tests/e2e/specs/amp-onboarding/template-mode.js
@@ -69,7 +69,7 @@ describe( 'Stepper item modifications', () => {
await clickMode( 'reader' );
const itemCount = await page.$$eval( '.amp-stepper__item', ( els ) => els.length );
- expect( itemCount ).toBe( 5 );
+ expect( itemCount ).toBe( 6 );
await expect( page ).toMatchElement( '.amp-stepper__item-title', { text: 'Theme Selection' } );
} );
@@ -78,7 +78,7 @@ describe( 'Stepper item modifications', () => {
await clickMode( 'transitional' );
const itemCount = await page.$$eval( '.amp-stepper__item', ( els ) => els.length );
- expect( itemCount ).toBe( 4 );
+ expect( itemCount ).toBe( 5 );
await expect( page ).not.toMatchElement( '.amp-stepper__item-title', { text: 'Theme Selection' } );
} );
diff --git a/tests/e2e/specs/amp-onboarding/transitional-recommendation.js b/tests/e2e/specs/amp-onboarding/transitional-recommendation.js
index 282153b18bc..aa8e33bb8c5 100644
--- a/tests/e2e/specs/amp-onboarding/transitional-recommendation.js
+++ b/tests/e2e/specs/amp-onboarding/transitional-recommendation.js
@@ -33,7 +33,7 @@ describe( 'Current active theme is reader theme and user is nontechnical', () =>
await moveToDoneScreen( { technical: false, readerTheme: 'twentytwenty', mode: 'reader' } );
const stepperItemCount = await page.$$eval( '.amp-stepper__item', ( els ) => els.length );
- expect( stepperItemCount ).toBe( 4 );
+ expect( stepperItemCount ).toBe( 5 );
// Wait for the settings to get saved.
await page.waitForTimeout( 1000 );
From 95e74d86ea6d4fb151c4bb95318d4ce380988257 Mon Sep 17 00:00:00 2001
From: Piotr Delawski
Date: Thu, 14 Oct 2021 18:04:33 +0200
Subject: [PATCH 068/120] Allow invalidating `scannableUrl` in the READY status
---
assets/src/components/site-scan-context-provider/index.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/assets/src/components/site-scan-context-provider/index.js b/assets/src/components/site-scan-context-provider/index.js
index 4c1ff58523e..b42232b183c 100644
--- a/assets/src/components/site-scan-context-provider/index.js
+++ b/assets/src/components/site-scan-context-provider/index.js
@@ -124,7 +124,7 @@ function siteScanReducer( state, action ) {
};
}
case ACTION_SCAN_INVALIDATE: {
- if ( ! [ STATUS_COMPLETED, STATUS_FAILED ].includes( state.status ) ) {
+ if ( ! [ STATUS_READY, STATUS_COMPLETED, STATUS_FAILED ].includes( state.status ) ) {
return state;
}
From b53c7bdc23fbbda3e2738ed34294f80de20aaaab Mon Sep 17 00:00:00 2001
From: Piotr Delawski
Date: Thu, 14 Oct 2021 19:40:17 +0200
Subject: [PATCH 069/120] Invalidate Site Scan results whenever specific
options change
---
.../site-scan-context-provider/index.js | 116 ++++++++++++------
assets/src/settings-page/site-scan.js | 5 +-
2 files changed, 82 insertions(+), 39 deletions(-)
diff --git a/assets/src/components/site-scan-context-provider/index.js b/assets/src/components/site-scan-context-provider/index.js
index b42232b183c..25bbebd8f03 100644
--- a/assets/src/components/site-scan-context-provider/index.js
+++ b/assets/src/components/site-scan-context-provider/index.js
@@ -3,7 +3,7 @@
*/
import { createContext, useCallback, useContext, useEffect, useMemo, useReducer, useRef } from '@wordpress/element';
import apiFetch from '@wordpress/api-fetch';
-import { usePrevious } from '@wordpress/compose';
+import isShallowEqual from '@wordpress/is-shallow-equal';
import { addQueryArgs } from '@wordpress/url';
/**
@@ -21,6 +21,23 @@ import { getSiteIssues } from './get-site-issues';
export const SiteScan = createContext();
+/**
+ * Array containing option keys that - when changed on the client side - should
+ * make the scan results stale.
+ *
+ * @type {string[]}
+ */
+const OPTIONS_INVALIDATING_SITE_SCAN = [
+ 'all_templates_supported',
+ 'supported_post_types',
+ 'supported_templates',
+ 'suppressed_plugins',
+ 'theme_support',
+];
+
+/**
+ * Site Scan Actions.
+ */
const ACTION_SCANNABLE_URLS_REQUEST = 'ACTION_SCANNABLE_URLS_REQUEST';
const ACTION_SCANNABLE_URLS_FETCH = 'ACTION_SCANNABLE_URLS_FETCH';
const ACTION_SCANNABLE_URLS_RECEIVE = 'ACTION_SCANNABLE_URLS_RECEIVE';
@@ -28,9 +45,11 @@ const ACTION_SCAN_INITIALIZE = 'ACTION_SCAN_INITIALIZE';
const ACTION_SCAN_VALIDATE_URL = 'ACTION_SCAN_VALIDATE_URL';
const ACTION_SCAN_RECEIVE_VALIDATION_ERRORS = 'ACTION_SCAN_RECEIVE_VALIDATION_ERRORS';
const ACTION_SCAN_NEXT_URL = 'ACTION_SCAN_NEXT_URL';
-const ACTION_SCAN_INVALIDATE = 'ACTION_SCAN_INVALIDATE';
const ACTION_SCAN_CANCEL = 'ACTION_SCAN_CANCEL';
+/**
+ * Site Scan Statuses.
+ */
const STATUS_REQUEST_SCANNABLE_URLS = 'STATUS_REQUEST_SCANNABLE_URLS';
const STATUS_FETCHING_SCANNABLE_URLS = 'STATUS_FETCHING_SCANNABLE_URLS';
const STATUS_READY = 'STATUS_READY';
@@ -40,6 +59,26 @@ const STATUS_COMPLETED = 'STATUS_COMPLETED';
const STATUS_FAILED = 'STATUS_FAILED';
const STATUS_CANCELLED = 'STATUS_CANCELLED';
+/**
+ * Initial Site Scan state.
+ *
+ * @type {Object}
+ */
+const INITIAL_STATE = {
+ cache: false,
+ currentlyScannedUrlIndex: 0,
+ frozenModifiedOptions: {},
+ scannableUrls: [],
+ status: '',
+};
+
+/**
+ * Site Scan Reducer.
+ *
+ * @param {Object} state Current state.
+ * @param {Object} action Action to call.
+ * @return {Object} New state.
+ */
function siteScanReducer( state, action ) {
switch ( action.type ) {
case ACTION_SCANNABLE_URLS_REQUEST: {
@@ -77,7 +116,8 @@ function siteScanReducer( state, action ) {
...state,
status: STATUS_IDLE,
cache: action.cache,
- currentlyScannedUrlIndex: initialState.currentlyScannedUrlIndex,
+ currentlyScannedUrlIndex: INITIAL_STATE.currentlyScannedUrlIndex,
+ frozenModifiedOptions: action.modifiedOptions,
};
}
case ACTION_SCAN_VALIDATE_URL: {
@@ -123,20 +163,6 @@ function siteScanReducer( state, action ) {
status: hasFailed ? STATUS_FAILED : STATUS_COMPLETED,
};
}
- case ACTION_SCAN_INVALIDATE: {
- if ( ! [ STATUS_READY, STATUS_COMPLETED, STATUS_FAILED ].includes( state.status ) ) {
- return state;
- }
-
- return {
- ...state,
- scannableUrls: state.scannableUrls.map( ( scannableUrl ) => ( {
- ...scannableUrl,
- stale: true,
- revalidated: false,
- } ) ),
- };
- }
case ACTION_SCAN_CANCEL: {
if ( ! [ STATUS_IDLE, STATUS_IN_PROGRESS ].includes( state.status ) ) {
return state;
@@ -145,7 +171,7 @@ function siteScanReducer( state, action ) {
return {
...state,
status: STATUS_CANCELLED,
- currentlyScannedUrlIndex: initialState.currentlyScannedUrlIndex,
+ currentlyScannedUrlIndex: INITIAL_STATE.currentlyScannedUrlIndex,
};
}
default: {
@@ -154,13 +180,6 @@ function siteScanReducer( state, action ) {
}
}
-const initialState = {
- cache: false,
- currentlyScannedUrlIndex: 0,
- scannableUrls: [],
- status: '',
-};
-
/**
* Context provider for site scanning.
*
@@ -180,12 +199,18 @@ export function SiteScanContextProvider( {
scannableUrlsRestPath,
validateNonce,
} ) {
- const { originalOptions: { theme_support: themeSupport } } = useContext( Options );
+ const {
+ modifiedOptions,
+ originalOptions: {
+ theme_support: themeSupport,
+ },
+ } = useContext( Options );
const { setAsyncError } = useAsyncError();
- const [ state, dispatch ] = useReducer( siteScanReducer, initialState );
+ const [ state, dispatch ] = useReducer( siteScanReducer, INITIAL_STATE );
const {
cache,
currentlyScannedUrlIndex,
+ frozenModifiedOptions,
scannableUrls,
status,
} = state;
@@ -194,13 +219,13 @@ export function SiteScanContextProvider( {
/**
* Memoize properties.
*/
- const { pluginIssues, themeIssues, stale } = useMemo( () => {
+ const { pluginIssues, themeIssues, hasStaleResults } = useMemo( () => {
// Skip if the scan is in progress.
if ( ! [ STATUS_READY, STATUS_COMPLETED ].includes( status ) ) {
return {
pluginIssues: [],
themeIssues: [],
- stale: undefined,
+ hasStaleResults: false,
};
}
@@ -210,10 +235,23 @@ export function SiteScanContextProvider( {
return {
pluginIssues: siteIssues.pluginIssues,
themeIssues: siteIssues.themeIssues,
- stale: Boolean( scannableUrls.find( ( scannableUrl ) => scannableUrl?.stale === true ) ),
+ hasStaleResults: Boolean( scannableUrls.find( ( scannableUrl ) => scannableUrl?.stale === true ) ),
};
}, [ scannableUrls, status ] );
+ const hasModifiedOptions = useMemo( () => {
+ return Boolean(
+ Object
+ .keys( modifiedOptions )
+ .find( ( key ) =>
+ OPTIONS_INVALIDATING_SITE_SCAN.includes( key ) &&
+ ! isShallowEqual( modifiedOptions[ key ], frozenModifiedOptions[ key ] ),
+ ),
+ );
+ }, [ frozenModifiedOptions, modifiedOptions ] );
+
+ const stale = hasModifiedOptions || hasStaleResults;
+
const previewPermalink = useMemo( () => {
return scannableUrls.find( ( { type } ) => type === 'home' )?.[ urlType ] || homeUrl;
}, [ homeUrl, scannableUrls, urlType ] );
@@ -246,23 +284,22 @@ export function SiteScanContextProvider( {
dispatch( {
type: ACTION_SCAN_INITIALIZE,
cache: args?.cache,
+ modifiedOptions,
} );
- }, [] );
+ }, [ modifiedOptions ] );
const cancelSiteScan = useCallback( () => {
dispatch( { type: ACTION_SCAN_CANCEL } );
}, [] );
/**
- * Cancel scan and invalidate current results whenever theme mode changes.
+ * Cancel scan and invalidate current results whenever options change.
*/
- const previousThemeSupport = usePrevious( themeSupport );
useEffect( () => {
- if ( previousThemeSupport && previousThemeSupport !== themeSupport ) {
+ if ( stale && [ STATUS_IN_PROGRESS, STATUS_IDLE ].includes( status ) ) {
dispatch( { type: ACTION_SCAN_CANCEL } );
- dispatch( { type: ACTION_SCAN_INVALIDATE } );
}
- }, [ previousThemeSupport, themeSupport ] );
+ }, [ stale, status ] );
/**
* Fetch scannable URLs from the REST endpoint.
@@ -343,8 +380,11 @@ export function SiteScanContextProvider( {
} );
}
} catch ( e ) {
- // Note that this doesn't catch failed HTTP responses.
- setAsyncError( e );
+ dispatch( {
+ type: ACTION_SCAN_RECEIVE_VALIDATION_ERRORS,
+ scannedUrlIndex: currentlyScannedUrlIndex,
+ error: true,
+ } );
}
dispatch( { type: ACTION_SCAN_NEXT_URL } );
diff --git a/assets/src/settings-page/site-scan.js b/assets/src/settings-page/site-scan.js
index d9ce69d31e5..806e2b5b41c 100644
--- a/assets/src/settings-page/site-scan.js
+++ b/assets/src/settings-page/site-scan.js
@@ -231,7 +231,10 @@ function SiteScanSummary() {
return (
<>
{ isReady ? (
-
+
{ stale
? __( 'Stale results. Rescan your site to ensure everything is working properly.', 'amp' )
From 745039e292d88e996623a6f08c1f7d41c47bf23a Mon Sep 17 00:00:00 2001
From: Piotr Delawski
Date: Fri, 15 Oct 2021 17:44:53 +0200
Subject: [PATCH 070/120] Use post or page URL for previewPermalink in Reader
mode
---
assets/src/components/site-scan-context-provider/index.js | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/assets/src/components/site-scan-context-provider/index.js b/assets/src/components/site-scan-context-provider/index.js
index 25bbebd8f03..4cf5d26b459 100644
--- a/assets/src/components/site-scan-context-provider/index.js
+++ b/assets/src/components/site-scan-context-provider/index.js
@@ -14,7 +14,7 @@ import PropTypes from 'prop-types';
/**
* Internal dependencies
*/
-import { STANDARD } from '../../common/constants';
+import { READER, STANDARD } from '../../common/constants';
import { useAsyncError } from '../../utils/use-async-error';
import { Options } from '../options-context-provider';
import { getSiteIssues } from './get-site-issues';
@@ -253,8 +253,10 @@ export function SiteScanContextProvider( {
const stale = hasModifiedOptions || hasStaleResults;
const previewPermalink = useMemo( () => {
- return scannableUrls.find( ( { type } ) => type === 'home' )?.[ urlType ] || homeUrl;
- }, [ homeUrl, scannableUrls, urlType ] );
+ const pageTypes = themeSupport === READER ? [ 'post', 'page' ] : [ 'home' ];
+
+ return scannableUrls.find( ( { type } ) => pageTypes.includes( type ) )?.[ urlType ] || homeUrl;
+ }, [ homeUrl, scannableUrls, themeSupport, urlType ] );
/**
* Preflight check.
From 61f958ccf6f91e75130180a56c9a7ab709e2e6f8 Mon Sep 17 00:00:00 2001
From: Piotr Delawski
Date: Fri, 15 Oct 2021 18:13:28 +0200
Subject: [PATCH 071/120] Ensure it's possible to go through entire Onboarding
Wizard in E2E tests
---
tests/e2e/utils/onboarding-wizard-utils.js | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/tests/e2e/utils/onboarding-wizard-utils.js b/tests/e2e/utils/onboarding-wizard-utils.js
index e9f8286a262..bb4338ae004 100644
--- a/tests/e2e/utils/onboarding-wizard-utils.js
+++ b/tests/e2e/utils/onboarding-wizard-utils.js
@@ -14,6 +14,7 @@ export async function goToOnboardingWizard() {
}
export async function clickNextButton() {
+ await page.waitForSelector( `${ NEXT_BUTTON_SELECTOR }:not([disabled])` );
await expect( page ).toClick( `${ NEXT_BUTTON_SELECTOR }:not([disabled])` );
}
@@ -27,12 +28,19 @@ export async function moveToTechnicalScreen() {
await expect( page ).toMatchElement( '.technical-background-option' );
}
-export async function moveToTemplateModeScreen( { technical } ) {
+export async function moveToSiteScanScreen( { technical } ) {
await moveToTechnicalScreen();
const radioSelector = technical ? '#technical-background-enable' : '#technical-background-disable';
await expect( page ).toClick( radioSelector );
+ await clickNextButton();
+ await expect( page ).toMatchElement( '.site-scan' );
+}
+
+export async function moveToTemplateModeScreen( { technical } ) {
+ await moveToSiteScanScreen( { technical } );
+
await clickNextButton();
await expect( page ).toMatchElement( '.template-mode-option' );
}
From 95b7f93174047b229126839dba99450b8ed288c4 Mon Sep 17 00:00:00 2001
From: Piotr Delawski
Date: Fri, 15 Oct 2021 18:14:01 +0200
Subject: [PATCH 072/120] Fix template mode recommendations verification (E2E
tests)
---
.../e2e/specs/amp-onboarding/template-mode.js | 86 +++++++++++++------
1 file changed, 60 insertions(+), 26 deletions(-)
diff --git a/tests/e2e/specs/amp-onboarding/template-mode.js b/tests/e2e/specs/amp-onboarding/template-mode.js
index 3d5ee6556e8..da5447e8343 100644
--- a/tests/e2e/specs/amp-onboarding/template-mode.js
+++ b/tests/e2e/specs/amp-onboarding/template-mode.js
@@ -49,14 +49,68 @@ describe( 'Template mode recommendations with reader theme active', () => {
await activateTheme( 'twentytwenty' );
} );
- it.each(
- [ 'technical', 'nontechnical' ],
- )( 'makes correct recommendations when user is not %s and the current theme is a reader theme', async ( technical ) => {
- await moveToTemplateModeScreen( { technical: technical === 'technical' } );
+ it( 'makes correct recommendations when user is not technical and the current theme is a reader theme', async () => {
+ await moveToTemplateModeScreen( { technical: false } );
+
+ // The Reader option should be collapsed.
+ await expect( page ).toMatchElement( '#template-mode-reader-container .components-panel__body-title button[aria-expanded="false"]' );
+
+ // The Transitional and Standard modes should be expanded and should contain a "Recommended" string.
+ await expect( page ).toMatchElement( '#template-mode-transitional-container .components-panel__body-title button[aria-expanded="true"]' );
+ const transitionalCopy = await page.$eval( '#template-mode-transitional-container .amp-drawer__panel-body', ( el ) => el.innerText );
+ expect( transitionalCopy ).toContain( 'Recommended' );
+
+ await expect( page ).toMatchElement( '#template-mode-standard-container .components-panel__body-title button[aria-expanded="true"]' );
+ const standardCopy = await page.$eval( '#template-mode-standard-container .amp-drawer__panel-body', ( el ) => el.innerText );
+ expect( standardCopy ).toContain( 'Recommended' );
+ } );
+
+ it( 'makes correct recommendations when user is technical and the current theme is a reader theme', async () => {
+ await moveToTemplateModeScreen( { technical: true } );
+
+ // The Reader and Transitional options should be collapsed.
+ await expect( page ).toMatchElement( '#template-mode-reader-container .components-panel__body-title button[aria-expanded="false"]' );
+ await expect( page ).toMatchElement( '#template-mode-transitional-container .components-panel__body-title button[aria-expanded="false"]' );
+
+ // The Standard mode should be expanded and should contain a success notice.
+ await expect( page ).toMatchElement( '#template-mode-standard-container .components-panel__body-title button[aria-expanded="true"]' );
+ await expect( page ).toMatchElement( '#template-mode-standard-container .amp-notice--success' );
+ } );
+} );
+
+describe( 'Template mode recommendations with non-reader-theme active', () => {
+ beforeEach( async () => {
+ await cleanUpSettings();
+ await installTheme( 'hestia' );
+ await activateTheme( 'hestia' );
+ } );
+
+ afterEach( async () => {
+ await deleteTheme( 'hestia', { newThemeSlug: 'twentytwenty' } );
+ } );
+
+ it( 'makes correct recommendations when user is not technical and the current theme is not a reader theme', async () => {
+ await moveToTemplateModeScreen( { technical: false } );
- await expect( page ).toMatchElement( '#template-mode-standard-container .amp-notice--info' );
- await expect( page ).toMatchElement( '#template-mode-transitional-container .amp-notice--success' );
+ // The Reader mode should be recommended.
+ await expect( page ).toMatchElement( '#template-mode-reader-container .components-panel__body-title button[aria-expanded="true"]' );
await expect( page ).toMatchElement( '#template-mode-reader-container .amp-notice--success' );
+
+ // The Standard and Transitional options should be collapsed.
+ await expect( page ).toMatchElement( '#template-mode-standard-container .components-panel__body-title button[aria-expanded="false"]' );
+ await expect( page ).toMatchElement( '#template-mode-transitional-container .components-panel__body-title button[aria-expanded="false"]' );
+ } );
+
+ it( 'makes correct recommendations when user is technical and the current theme is not a reader theme', async () => {
+ await moveToTemplateModeScreen( { technical: true } );
+
+ // The Standard mode should be recommended.
+ await expect( page ).toMatchElement( '#template-mode-standard-container .components-panel__body-title button[aria-expanded="true"]' );
+ await expect( page ).toMatchElement( '#template-mode-standard-container .amp-notice--success' );
+
+ // The Reader and Transitional options should be collapsed.
+ await expect( page ).toMatchElement( '#template-mode-reader-container .components-panel__body-title button[aria-expanded="false"]' );
+ await expect( page ).toMatchElement( '#template-mode-transitional-container .components-panel__body-title button[aria-expanded="false"]' );
} );
} );
@@ -83,23 +137,3 @@ describe( 'Stepper item modifications', () => {
await expect( page ).not.toMatchElement( '.amp-stepper__item-title', { text: 'Theme Selection' } );
} );
} );
-
-describe( 'Template mode recommendations with non-reader-theme active', () => {
- beforeEach( async () => {
- await cleanUpSettings();
- await installTheme( 'hestia' );
- await activateTheme( 'hestia' );
- } );
-
- afterEach( async () => {
- await deleteTheme( 'hestia', { newThemeSlug: 'twentytwenty' } );
- } );
-
- it( 'makes correct recommendations when user is not technical and the current theme is not a reader theme', async () => {
- await moveToTemplateModeScreen( { technical: false } );
-
- await expect( page ).toMatchElement( '#template-mode-standard-container .amp-notice--info' );
- await expect( page ).toMatchElement( '#template-mode-transitional-container .amp-notice--info' );
- await expect( page ).toMatchElement( '#template-mode-reader-container .amp-notice--success' );
- } );
-} );
From 19bed4649af796837756eac01ca092873e3d8afe Mon Sep 17 00:00:00 2001
From: Piotr Delawski
Date: Fri, 15 Oct 2021 18:26:17 +0200
Subject: [PATCH 073/120] Clean up `transitional-recommendation` spec - overlap
with `template-mode` spec
---
.../transitional-recommendation.js | 17 ++---------------
tests/e2e/utils/onboarding-wizard-utils.js | 8 +++++---
2 files changed, 7 insertions(+), 18 deletions(-)
diff --git a/tests/e2e/specs/amp-onboarding/transitional-recommendation.js b/tests/e2e/specs/amp-onboarding/transitional-recommendation.js
index aa8e33bb8c5..70320866ebd 100644
--- a/tests/e2e/specs/amp-onboarding/transitional-recommendation.js
+++ b/tests/e2e/specs/amp-onboarding/transitional-recommendation.js
@@ -1,13 +1,10 @@
/**
* Internal dependencies
*/
-import { moveToReaderThemesScreen, moveToTemplateModeScreen, moveToDoneScreen } from '../../utils/onboarding-wizard-utils';
+import { moveToReaderThemesScreen, moveToDoneScreen } from '../../utils/onboarding-wizard-utils';
/**
- * When a site has a Reader theme already set as the active theme (e.g. Twenty Twenty), when the user expresses they
- * are non-technical then both the Reader mode and the Transitional mode should show as ✅ recommended options.
- *
- * Additionally, when selecting Reader mode, the list of themes should no longer omit the active theme from the list.
+ * When selecting Reader mode, the list of themes should no longer omit the active theme from the list.
* Instead, if the user selects the active theme to be the Reader theme, then the template mode should be automatically
* switched from reader to transitional, and a notice can appear on the summary screen to make them aware of this.
*
@@ -16,13 +13,6 @@ import { moveToReaderThemesScreen, moveToTemplateModeScreen, moveToDoneScreen }
* @see https://github.com/ampproject/amp-wp/issues/4975
*/
describe( 'Current active theme is reader theme and user is nontechnical', () => {
- it( 'correctly recommends transitional when the user is nontechnical and the active theme is a reader theme', async () => {
- await moveToTemplateModeScreen( { technical: false } );
-
- await expect( '.amp-notice--info' ).countToBe( 1 ); // Standard.
- await expect( '.amp-notice--success' ).countToBe( 2 ); // Reader and transitional.
- } );
-
it( 'includes active theme in reader theme list', async () => {
await moveToReaderThemesScreen( { technical: false } );
@@ -35,9 +25,6 @@ describe( 'Current active theme is reader theme and user is nontechnical', () =>
const stepperItemCount = await page.$$eval( '.amp-stepper__item', ( els ) => els.length );
expect( stepperItemCount ).toBe( 5 );
- // Wait for the settings to get saved.
- await page.waitForTimeout( 1000 );
-
await expect( page ).toMatchElement( 'p', { text: /transitional mode/i } );
await expect( page ).toMatchElement( '.amp-notice--info', { text: /switched to Transitional/i } );
} );
diff --git a/tests/e2e/utils/onboarding-wizard-utils.js b/tests/e2e/utils/onboarding-wizard-utils.js
index bb4338ae004..343148ddbe8 100644
--- a/tests/e2e/utils/onboarding-wizard-utils.js
+++ b/tests/e2e/utils/onboarding-wizard-utils.js
@@ -84,9 +84,11 @@ export async function moveToDoneScreen( { technical = true, mode, readerTheme =
await clickMode( mode );
}
- await clickNextButton();
-
- await page.waitForSelector( '.done' );
+ await Promise.all( [
+ clickNextButton(),
+ page.waitForResponse( ( response ) => response.url().includes( '/wp-json/amp/v1/options' ) ),
+ page.waitForSelector( '.done' ),
+ ] );
}
export async function completeWizard( { technical = true, mode, readerTheme = 'legacy' } ) {
From c1f5f41ffda409864041d3a3774248b8cabddf78 Mon Sep 17 00:00:00 2001
From: Piotr Delawski
Date: Mon, 18 Oct 2021 19:02:57 +0200
Subject: [PATCH 074/120] Add E2E tests for site scanning in Wizard and
Settings page
---
.../pages/site-scan/index.js | 2 +-
assets/src/settings-page/site-scan.js | 2 +-
tests/e2e/specs/admin/amp-options.js | 16 ++-----
tests/e2e/specs/admin/site-scan.js | 44 +++++++++++++++++++
tests/e2e/specs/amp-onboarding/site-scan.js | 39 ++++++++++++++++
tests/e2e/utils/amp-settings-utils.js | 20 +++++++++
tests/e2e/utils/site-scan-utils.js | 18 ++++++++
7 files changed, 127 insertions(+), 14 deletions(-)
create mode 100644 tests/e2e/specs/admin/site-scan.js
create mode 100644 tests/e2e/specs/amp-onboarding/site-scan.js
create mode 100644 tests/e2e/utils/amp-settings-utils.js
create mode 100644 tests/e2e/utils/site-scan-utils.js
diff --git a/assets/src/onboarding-wizard/pages/site-scan/index.js b/assets/src/onboarding-wizard/pages/site-scan/index.js
index c1f65321a17..6d625486f36 100644
--- a/assets/src/onboarding-wizard/pages/site-scan/index.js
+++ b/assets/src/onboarding-wizard/pages/site-scan/index.js
@@ -140,7 +140,7 @@ export function SiteScan() {
? 100
: ( currentlyScannedUrlIndex / scannableUrls.length * 100 )
} />
-
+
{ isCompleted
? __( 'Scan complete', 'amp' )
: sprintf(
diff --git a/assets/src/settings-page/site-scan.js b/assets/src/settings-page/site-scan.js
index 806e2b5b41c..ad715144490 100644
--- a/assets/src/settings-page/site-scan.js
+++ b/assets/src/settings-page/site-scan.js
@@ -200,7 +200,7 @@ function SiteScanInProgress() {
? 100
: ( currentlyScannedUrlIndex / scannableUrls.length * 100 )
} />
-
+
{ isCompleted
? __( 'Scan complete', 'amp' )
: sprintf(
diff --git a/tests/e2e/specs/admin/amp-options.js b/tests/e2e/specs/admin/amp-options.js
index b2b93e8aa55..5e4a3dc9b06 100644
--- a/tests/e2e/specs/admin/amp-options.js
+++ b/tests/e2e/specs/admin/amp-options.js
@@ -7,6 +7,7 @@ import { visitAdminPage, activateTheme, installTheme } from '@wordpress/e2e-test
* Internal dependencies
*/
import { completeWizard, cleanUpSettings, clickMode, scrollToElement } from '../../utils/onboarding-wizard-utils';
+import { setTemplateMode } from '../../utils/amp-settings-utils';
describe( 'AMP settings screen newly activated', () => {
beforeEach( async () => {
@@ -169,15 +170,6 @@ describe( 'AMP settings screen Review panel', () => {
await cleanUpSettings();
} );
- async function changeAndSaveTemplateMode( mode ) {
- await clickMode( mode );
-
- await Promise.all( [
- scrollToElement( { selector: '.amp-settings-nav button[type="submit"]', click: true } ),
- page.waitForResponse( ( response ) => response.url().includes( '/wp-json/amp/v1/options' ), { timeout: 10000 } ),
- ] );
- }
-
it( 'is present on the page', async () => {
await page.waitForSelector( '.settings-site-review' );
await expect( page ).toMatchElement( 'h2', { text: 'Review' } );
@@ -188,7 +180,7 @@ describe( 'AMP settings screen Review panel', () => {
} );
it( 'button redirects to an AMP page in transitional mode', async () => {
- await changeAndSaveTemplateMode( 'transitional' );
+ await setTemplateMode( 'transitional' );
await expect( page ).toClick( 'a', { text: 'Browse Site' } );
await page.waitForNavigation();
@@ -206,7 +198,7 @@ describe( 'AMP settings screen Review panel', () => {
} );
it( 'button redirects to an AMP page in standard mode', async () => {
- await changeAndSaveTemplateMode( 'standard' );
+ await setTemplateMode( 'standard' );
await expect( page ).toClick( 'a', { text: 'Browse Site' } );
await page.waitForNavigation();
@@ -229,7 +221,7 @@ describe( 'AMP settings screen Review panel', () => {
await page.waitForSelector( '#amp-settings-root' );
await expect( page ).not.toMatchElement( '.settings-site-review' );
- await changeAndSaveTemplateMode( 'standard' );
+ await setTemplateMode( 'standard' );
await page.waitForSelector( '.settings-site-review' );
await expect( page ).toMatchElement( 'h2', { text: 'Review' } );
diff --git a/tests/e2e/specs/admin/site-scan.js b/tests/e2e/specs/admin/site-scan.js
new file mode 100644
index 00000000000..8cb17dcf023
--- /dev/null
+++ b/tests/e2e/specs/admin/site-scan.js
@@ -0,0 +1,44 @@
+/**
+ * WordPress dependencies
+ */
+import { visitAdminPage } from '@wordpress/e2e-test-utils';
+
+/**
+ * Internal dependencies
+ */
+import { cleanUpSettings } from '../../utils/onboarding-wizard-utils';
+import { setTemplateMode } from '../../utils/amp-settings-utils';
+import { testSiteScanning } from '../../utils/site-scan-utils';
+
+describe( 'AMP settings screen Site Scan panel', () => {
+ beforeEach( async () => {
+ await visitAdminPage( 'admin.php', 'page=amp-options' );
+ } );
+
+ afterEach( async () => {
+ await cleanUpSettings();
+ } );
+
+ it( 'is present on the page and allows triggering a site scan', async () => {
+ await page.waitForSelector( '#template-modes' );
+
+ // Switch template mode so that we have stale results for sure.
+ const selectedMode = await page.$eval( '#template-modes input[checked]', ( el ) => el.getAttribute( 'id' ) );
+ await setTemplateMode( selectedMode.includes( 'transitional' ) ? 'standard' : 'transitional' );
+
+ await page.waitForSelector( '.settings-site-scan' );
+ await expect( page ).toMatchElement( 'h2', { text: 'Site Scan' } );
+ await expect( page ).toMatchElement( 'button.is-primary', { text: 'Rescan Site' } );
+
+ // Start site scan.
+ await expect( page ).toClick( 'button.is-primary', { text: 'Rescan Site' } );
+
+ await testSiteScanning( {
+ statusElementClassName: 'settings-site-scan__status',
+ isAmpFirst: false,
+ } );
+
+ await page.waitForSelector( '.settings-site-scan__footer a.is-primary' );
+ await expect( page ).toMatchElement( '.settings-site-scan__footer a.is-primary', { text: 'Browse Site' } );
+ } );
+} );
diff --git a/tests/e2e/specs/amp-onboarding/site-scan.js b/tests/e2e/specs/amp-onboarding/site-scan.js
new file mode 100644
index 00000000000..356c6993346
--- /dev/null
+++ b/tests/e2e/specs/amp-onboarding/site-scan.js
@@ -0,0 +1,39 @@
+/**
+ * Internal dependencies
+ */
+import {
+ moveToSiteScanScreen,
+ testNextButton,
+ testPreviousButton,
+} from '../../utils/onboarding-wizard-utils';
+import { testSiteScanning } from '../../utils/site-scan-utils';
+
+describe( 'Site Scan', () => {
+ beforeEach( async () => {
+ await moveToSiteScanScreen( { technical: true } );
+ } );
+
+ it( 'should start a site scan immediately', async () => {
+ await page.waitForSelector( '.amp-onboarding-wizard-panel h1' );
+
+ const screenHeading = await page.$eval( '.amp-onboarding-wizard-panel h1', ( el ) => el.innerText );
+ expect( screenHeading ).toContain( 'Site Scan' );
+
+ const scanInProgressHandle = await page.waitForXPath( `//p[contains(text(), 'Please wait a minute')]` );
+ expect( scanInProgressHandle ).not.toBeNull();
+
+ testNextButton( { text: 'Next', disabled: true } );
+ testPreviousButton( { text: 'Previous' } );
+
+ await testSiteScanning( {
+ statusElementClassName: 'site-scan__status',
+ isAmpFirst: true,
+ } );
+
+ const scanCompleteHandle = await page.waitForXPath( `//p[@class='site-scan__heading'][contains(text(), 'Scan complete')]` );
+ expect( scanCompleteHandle ).not.toBeNull();
+
+ testNextButton( { text: 'Next' } );
+ testPreviousButton( { text: 'Previous' } );
+ } );
+} );
diff --git a/tests/e2e/utils/amp-settings-utils.js b/tests/e2e/utils/amp-settings-utils.js
new file mode 100644
index 00000000000..86f7e56b0ce
--- /dev/null
+++ b/tests/e2e/utils/amp-settings-utils.js
@@ -0,0 +1,20 @@
+/**
+ * Internal dependencies
+ */
+import { scrollToElement } from './onboarding-wizard-utils';
+
+export async function setTemplateMode( mode ) {
+ // Set template mode.
+ const modeSelector = `#template-mode-${ mode }-container input`;
+
+ await page.waitForSelector( modeSelector );
+ await scrollToElement( { selector: modeSelector, click: true } );
+
+ // Save options and wait for the request to succeed.
+ const saveButtonSelector = '.amp-settings-nav button[type="submit"]';
+
+ await page.waitForSelector( saveButtonSelector );
+ await scrollToElement( { selector: saveButtonSelector, click: true } );
+
+ await page.waitForResponse( ( response ) => response.url().includes( '/wp-json/amp/v1/options' ) );
+}
diff --git a/tests/e2e/utils/site-scan-utils.js b/tests/e2e/utils/site-scan-utils.js
new file mode 100644
index 00000000000..a96b9ab3402
--- /dev/null
+++ b/tests/e2e/utils/site-scan-utils.js
@@ -0,0 +1,18 @@
+export async function testSiteScanning( { statusElementClassName, isAmpFirst } ) {
+ const statusTextRegex = /^Scanning \d+\/([\d]+) URLs/;
+ const statusText = await page.$eval( `.${ statusElementClassName }`, ( el ) => el.innerText );
+
+ expect( statusText ).toMatch( statusTextRegex );
+
+ const scannableUrlsCount = Number( statusText.match( statusTextRegex )[ 1 ] );
+ const expectedParams = [
+ 'amp_validate[nonce]',
+ 'amp_validate[omit_stylesheets]',
+ 'amp_validate[cache_bust]',
+ ].map( encodeURI );
+
+ await Promise.all( [ ...Array( scannableUrlsCount ) ].map( ( url, index ) => ( [
+ page.waitForResponse( ( response ) => isAmpFirst === response.url().includes( 'amp-first' ) && expectedParams.every( ( param ) => response.url().includes( param ) ) ),
+ page.waitForXPath( `//p[@class='${ statusElementClassName }'][contains(text(), 'Scanning ${ index + 1 }/${ scannableUrlsCount } URLs')]` ),
+ ] ) ).flat() );
+}
From 640c81bd2878a5f1e1cbd319b82f23d3fba8eca9 Mon Sep 17 00:00:00 2001
From: Piotr Delawski
Date: Tue, 19 Oct 2021 12:50:13 +0200
Subject: [PATCH 075/120] Extract AMP Setting page Review panel E2E tests to
separate file
---
tests/e2e/specs/admin/amp-options.js | 92 -------------------
tests/e2e/specs/admin/site-review-panel.js | 83 +++++++++++++++++
.../{site-scan.js => site-scan-panel.js} | 0
3 files changed, 83 insertions(+), 92 deletions(-)
create mode 100644 tests/e2e/specs/admin/site-review-panel.js
rename tests/e2e/specs/admin/{site-scan.js => site-scan-panel.js} (100%)
diff --git a/tests/e2e/specs/admin/amp-options.js b/tests/e2e/specs/admin/amp-options.js
index 5e4a3dc9b06..7140a83969e 100644
--- a/tests/e2e/specs/admin/amp-options.js
+++ b/tests/e2e/specs/admin/amp-options.js
@@ -7,7 +7,6 @@ import { visitAdminPage, activateTheme, installTheme } from '@wordpress/e2e-test
* Internal dependencies
*/
import { completeWizard, cleanUpSettings, clickMode, scrollToElement } from '../../utils/onboarding-wizard-utils';
-import { setTemplateMode } from '../../utils/amp-settings-utils';
describe( 'AMP settings screen newly activated', () => {
beforeEach( async () => {
@@ -136,94 +135,3 @@ describe( 'Saving', () => {
await testSave();
} );
} );
-
-describe( 'AMP settings screen Review panel', () => {
- let testPost;
-
- beforeAll( async () => {
- await visitAdminPage( 'admin.php', 'page=amp-options' );
-
- testPost = await page.evaluate( () => wp.apiFetch( {
- path: '/wp/v2/posts',
- method: 'POST',
- data: { title: 'Test Post', status: 'publish' },
- } ) );
- } );
-
- afterAll( async () => {
- await visitAdminPage( 'admin.php', 'page=amp-options' );
-
- if ( testPost?.id ) {
- await page.evaluate( ( id ) => wp.apiFetch( {
- path: `/wp/v2/posts/${ id }`,
- method: 'DELETE',
- data: { force: true },
- } ), testPost.id );
- }
- } );
-
- beforeEach( async () => {
- await visitAdminPage( 'admin.php', 'page=amp-options' );
- } );
-
- afterEach( async () => {
- await cleanUpSettings();
- } );
-
- it( 'is present on the page', async () => {
- await page.waitForSelector( '.settings-site-review' );
- await expect( page ).toMatchElement( 'h2', { text: 'Review' } );
- await expect( page ).toMatchElement( 'h3', { text: 'Need help?' } );
- await expect( page ).toMatchElement( '.settings-site-review__list li', { text: /support forums/i } );
- await expect( page ).toMatchElement( '.settings-site-review__list li', { text: /different template mode/i } );
- await expect( page ).toMatchElement( '.settings-site-review__list li', { text: /how the AMP plugin works/i } );
- } );
-
- it( 'button redirects to an AMP page in transitional mode', async () => {
- await setTemplateMode( 'transitional' );
-
- await expect( page ).toClick( 'a', { text: 'Browse Site' } );
- await page.waitForNavigation();
-
- await page.waitForSelector( 'html[amp]' );
- await expect( page ).toMatchElement( 'html[amp]' );
- } );
-
- it( 'button redirects to an AMP page in reader mode', async () => {
- await expect( page ).toClick( 'a', { text: 'Browse Site' } );
- await page.waitForNavigation();
-
- await page.waitForSelector( 'html[amp]' );
- await expect( page ).toMatchElement( 'html[amp]' );
- } );
-
- it( 'button redirects to an AMP page in standard mode', async () => {
- await setTemplateMode( 'standard' );
-
- await expect( page ).toClick( 'a', { text: 'Browse Site' } );
- await page.waitForNavigation();
-
- await page.waitForSelector( 'html[amp]' );
- await expect( page ).toMatchElement( 'html[amp]' );
- } );
-
- it( 'can be dismissed and shows up again only after a template mode change', async () => {
- await page.waitForSelector( '.settings-site-review' );
- await expect( page ).toMatchElement( 'button', { text: 'Dismiss' } );
- await expect( page ).toClick( 'button', { text: 'Dismiss' } );
-
- // Give the Review panel some time disappear.
- await page.waitForTimeout( 100 );
- await expect( page ).not.toMatchElement( '.settings-site-review' );
-
- // There should be no Review panel after page reload.
- await visitAdminPage( 'admin.php', 'page=amp-options' );
- await page.waitForSelector( '#amp-settings-root' );
- await expect( page ).not.toMatchElement( '.settings-site-review' );
-
- await setTemplateMode( 'standard' );
-
- await page.waitForSelector( '.settings-site-review' );
- await expect( page ).toMatchElement( 'h2', { text: 'Review' } );
- } );
-} );
diff --git a/tests/e2e/specs/admin/site-review-panel.js b/tests/e2e/specs/admin/site-review-panel.js
new file mode 100644
index 00000000000..65d13e43969
--- /dev/null
+++ b/tests/e2e/specs/admin/site-review-panel.js
@@ -0,0 +1,83 @@
+/**
+ * WordPress dependencies
+ */
+import { visitAdminPage } from '@wordpress/e2e-test-utils';
+
+/**
+ * Internal dependencies
+ */
+import { cleanUpSettings, clickMode, scrollToElement } from '../../utils/onboarding-wizard-utils';
+import { setTemplateMode } from '../../utils/amp-settings-utils';
+
+describe( 'AMP settings screen Review panel', () => {
+ beforeEach( async () => {
+ await visitAdminPage( 'admin.php', 'page=amp-options' );
+ } );
+
+ afterEach( async () => {
+ await cleanUpSettings();
+ } );
+
+ it( 'is present on the page', async () => {
+ await page.waitForSelector( '.settings-site-review' );
+ await expect( page ).toMatchElement( 'h2', { text: 'Review' } );
+ await expect( page ).toMatchElement( 'h3', { text: 'Need help?' } );
+ await expect( page ).toMatchElement( '.settings-site-review__list li', { text: /support forums/i } );
+ await expect( page ).toMatchElement( '.settings-site-review__list li', { text: /different template mode/i } );
+ await expect( page ).toMatchElement( '.settings-site-review__list li', { text: /how the AMP plugin works/i } );
+ } );
+
+ it.each( [
+ 'transitional',
+ 'standard',
+ 'reader',
+ ] )( 'button redirects to an AMP page in %s mode', async ( mode ) => {
+ // Make sure the template mode needs to be changed for a test.
+ await page.waitForSelector( '#template-modes' );
+
+ const selectedMode = await page.$eval( '#template-modes input[checked]', ( el ) => el.getAttribute( 'id' ) );
+
+ if ( ! selectedMode.includes( mode ) ) {
+ await setTemplateMode( mode );
+ }
+
+ // Click "Browse Site" button.
+ const selector = '.settings-site-review__actions a.is-primary';
+
+ await page.waitForSelector( selector );
+ await scrollToElement( { selector, click: true } );
+
+ await page.waitForNavigation();
+
+ const htmlAttributes = await page.$eval( 'html', ( el ) => el.getAttributeNames() );
+ await expect( htmlAttributes ).toContain( 'amp' );
+ } );
+
+ it( 'can be dismissed and shows up again only after a template mode change', async () => {
+ const dismissButtonSelector = '.settings-site-review__actions button.is-link';
+
+ await page.waitForSelector( dismissButtonSelector );
+
+ // Click the "Dismiss" button and wait for the HTTP response.
+ await Promise.all( [
+ scrollToElement( { selector: dismissButtonSelector, click: true } ),
+ page.waitForResponse( ( response ) => response.url().includes( '/wp/v2/users/me' ) ),
+ ] );
+
+ await expect( page ).not.toMatchElement( '.settings-site-review' );
+
+ // There should be no Review panel after page reload.
+ await visitAdminPage( 'admin.php', 'page=amp-options' );
+ await page.waitForSelector( '#amp-settings-root' );
+ await expect( page ).not.toMatchElement( '.settings-site-review' );
+
+ await clickMode( 'standard' );
+
+ await expect( page ).toClick( 'button', { text: 'Save' } );
+ await page.waitForSelector( '.amp-save-success-notice' );
+ await expect( page ).toMatchElement( '.amp-save-success-notice', { text: 'Saved' } );
+
+ await page.waitForSelector( '.settings-site-review' );
+ await expect( page ).toMatchElement( 'h2', { text: 'Review' } );
+ } );
+} );
diff --git a/tests/e2e/specs/admin/site-scan.js b/tests/e2e/specs/admin/site-scan-panel.js
similarity index 100%
rename from tests/e2e/specs/admin/site-scan.js
rename to tests/e2e/specs/admin/site-scan-panel.js
From ce4d1c6abb31fef07d8529a51dd754c586c612c5 Mon Sep 17 00:00:00 2001
From: Piotr Delawski
Date: Wed, 20 Oct 2021 15:07:35 +0200
Subject: [PATCH 076/120] Add E2E tests for Site Scan results on AMP Settings
screen
---
.../site-scan-results/plugins-with-issues.js | 1 +
.../site-scan-results/themes-with-issues.js | 3 +-
tests/e2e/specs/admin/site-scan-panel.js | 123 +++++++++++++++---
tests/e2e/utils/amp-settings-utils.js | 63 ++++++++-
tests/e2e/utils/site-scan-utils.js | 19 ++-
5 files changed, 182 insertions(+), 27 deletions(-)
diff --git a/assets/src/components/site-scan-results/plugins-with-issues.js b/assets/src/components/site-scan-results/plugins-with-issues.js
index 417e54ab187..fd3c0ac44cf 100644
--- a/assets/src/components/site-scan-results/plugins-with-issues.js
+++ b/assets/src/components/site-scan-results/plugins-with-issues.js
@@ -33,6 +33,7 @@ export function PluginsWithIssues( { issues = [], validatedUrlsLink, ...props }
count={ issues.length }
sources={ sources }
validatedUrlsLink={ validatedUrlsLink }
+ className="site-scan-results--plugins"
{ ...props }
/>
);
diff --git a/assets/src/components/site-scan-results/themes-with-issues.js b/assets/src/components/site-scan-results/themes-with-issues.js
index 45b5d06f42e..748831e6d2e 100644
--- a/assets/src/components/site-scan-results/themes-with-issues.js
+++ b/assets/src/components/site-scan-results/themes-with-issues.js
@@ -28,11 +28,12 @@ export function ThemesWithIssues( { issues = [], validatedUrlsLink, ...props } )
return (
}
count={ issues.length }
sources={ sources }
validatedUrlsLink={ validatedUrlsLink }
+ className="site-scan-results--themes"
{ ...props }
/>
);
diff --git a/tests/e2e/specs/admin/site-scan-panel.js b/tests/e2e/specs/admin/site-scan-panel.js
index 8cb17dcf023..3ed62be9bc7 100644
--- a/tests/e2e/specs/admin/site-scan-panel.js
+++ b/tests/e2e/specs/admin/site-scan-panel.js
@@ -1,44 +1,131 @@
/**
* WordPress dependencies
*/
-import { visitAdminPage } from '@wordpress/e2e-test-utils';
+import {
+ activateTheme,
+ deleteTheme,
+ installTheme,
+ visitAdminPage,
+} from '@wordpress/e2e-test-utils';
/**
* Internal dependencies
*/
-import { cleanUpSettings } from '../../utils/onboarding-wizard-utils';
-import { setTemplateMode } from '../../utils/amp-settings-utils';
+import {
+ activatePlugin,
+ deactivatePlugin,
+ installPlugin,
+ setTemplateMode,
+ uninstallPlugin,
+} from '../../utils/amp-settings-utils';
+import { cleanUpSettings, scrollToElement } from '../../utils/onboarding-wizard-utils';
import { testSiteScanning } from '../../utils/site-scan-utils';
describe( 'AMP settings screen Site Scan panel', () => {
- beforeEach( async () => {
- await visitAdminPage( 'admin.php', 'page=amp-options' );
+ beforeAll( async () => {
+ await installTheme( 'twentytwentyone' );
+ await installTheme( 'hestia' );
+ await installPlugin( 'autoptimize' );
+ } );
+
+ afterAll( async () => {
+ await cleanUpSettings();
+ await deleteTheme( 'hestia', { newThemeSlug: 'twentytwentyone' } );
+ await uninstallPlugin( 'autoptimize' );
} );
- afterEach( async () => {
+ beforeEach( async () => {
await cleanUpSettings();
} );
- it( 'is present on the page and allows triggering a site scan', async () => {
+ async function triggerSiteRescan() {
await page.waitForSelector( '#template-modes' );
// Switch template mode so that we have stale results for sure.
- const selectedMode = await page.$eval( '#template-modes input[checked]', ( el ) => el.getAttribute( 'id' ) );
- await setTemplateMode( selectedMode.includes( 'transitional' ) ? 'standard' : 'transitional' );
+ await setTemplateMode( 'transitional' );
await page.waitForSelector( '.settings-site-scan' );
await expect( page ).toMatchElement( 'h2', { text: 'Site Scan' } );
- await expect( page ).toMatchElement( 'button.is-primary', { text: 'Rescan Site' } );
- // Start site scan.
- await expect( page ).toClick( 'button.is-primary', { text: 'Rescan Site' } );
+ // Start the site scan.
+ await Promise.all( [
+ scrollToElement( { selector: '.settings-site-scan__footer button.is-primary', click: true } ),
+ testSiteScanning( {
+ statusElementClassName: 'settings-site-scan__status',
+ isAmpFirst: false,
+ } ),
+ expect( page ).toMatchElement( '.settings-site-scan__footer a.is-primary', { text: 'Browse Site', timeout: 10000 } ),
+ ] );
+ }
+
+ it( 'does not list issues if an AMP compatible theme is activated', async () => {
+ await activateTheme( 'twentytwentyone' );
+
+ await visitAdminPage( 'admin.php', 'page=amp-options' );
+
+ await triggerSiteRescan();
+
+ await expect( page ).toMatchElement( '.settings-site-scan .amp-notice--success' );
+
+ await expect( page ).not.toMatchElement( '.site-scan-results--themes' );
+ await expect( page ).not.toMatchElement( '.site-scan-results--plugins' );
+
+ // Switch template mode to check if the scan results are marked as stale.
+ await setTemplateMode( 'standard' );
+
+ await expect( page ).toMatchElement( '.settings-site-scan .amp-notice--info', { text: /^Stale results/ } );
+ } );
+
+ it( 'lists Hestia theme as causing AMP incompatibility', async () => {
+ await activateTheme( 'hestia' );
+
+ await visitAdminPage( 'admin.php', 'page=amp-options' );
+
+ await expect( page ).toMatchElement( '.settings-site-scan .amp-notice--info', { text: /^Stale results/ } );
+
+ await triggerSiteRescan();
+
+ await expect( page ).toMatchElement( '.site-scan-results--themes .site-scan-results__heading[data-badge-content="1"]', { text: /^Themes/ } );
+ await expect( page ).toMatchElement( '.site-scan-results--themes .site-scan-results__source-name', { text: /Hestia/ } );
+ } );
+
+ it( 'lists Autoptimize plugin as causing AMP incompatibility', async () => {
+ await activateTheme( 'twentytwentyone' );
+ await activatePlugin( 'autoptimize' );
+
+ await visitAdminPage( 'admin.php', 'page=amp-options' );
+
+ await expect( page ).toMatchElement( '.settings-site-scan .amp-notice--info', { text: /^Stale results/ } );
+
+ await triggerSiteRescan();
+
+ await expect( page ).not.toMatchElement( '.site-scan-results--themes' );
+
+ await expect( page ).toMatchElement( '.site-scan-results--plugins .site-scan-results__heading[data-badge-content="1"]', { text: /^Plugins/ } );
+ await expect( page ).toMatchElement( '.site-scan-results--plugins .site-scan-results__source-name', { text: /Autoptimize/ } );
+
+ await deactivatePlugin( 'autoptimize' );
+ } );
+
+ it( 'lists Hestia theme and Autoptimize plugin for causing AMP incompatibilities', async () => {
+ await activateTheme( 'hestia' );
+ await activatePlugin( 'autoptimize' );
+
+ await visitAdminPage( 'admin.php', 'page=amp-options' );
+
+ await expect( page ).toMatchElement( '.settings-site-scan .amp-notice--info', { text: /^Stale results/ } );
+
+ await triggerSiteRescan();
+
+ await expect( page ).toMatchElement( '.site-scan-results--themes' );
+ await expect( page ).toMatchElement( '.site-scan-results--plugins' );
+
+ const totalIssuesCount = await page.$$eval( '.site-scan-results__source', ( sources ) => sources.length );
+ expect( totalIssuesCount ).toBe( 2 );
- await testSiteScanning( {
- statusElementClassName: 'settings-site-scan__status',
- isAmpFirst: false,
- } );
+ await expect( page ).toMatchElement( '.site-scan-results--themes .site-scan-results__source-name', { text: /Hestia/ } );
+ await expect( page ).toMatchElement( '.site-scan-results--plugins .site-scan-results__source-name', { text: /Autoptimize/ } );
- await page.waitForSelector( '.settings-site-scan__footer a.is-primary' );
- await expect( page ).toMatchElement( '.settings-site-scan__footer a.is-primary', { text: 'Browse Site' } );
+ await deactivatePlugin( 'autoptimize' );
} );
} );
diff --git a/tests/e2e/utils/amp-settings-utils.js b/tests/e2e/utils/amp-settings-utils.js
index 86f7e56b0ce..5d09e1cf0b1 100644
--- a/tests/e2e/utils/amp-settings-utils.js
+++ b/tests/e2e/utils/amp-settings-utils.js
@@ -1,3 +1,16 @@
+/**
+ * WordPress dependencies
+ */
+import {
+ activatePlugin as _activatePlugin,
+ deactivatePlugin as _deactivatePlugin,
+ installPlugin as _installPlugin,
+ switchUserToAdmin,
+ switchUserToTest,
+ uninstallPlugin as _uninstallPlugin,
+ visitAdminPage,
+} from '@wordpress/e2e-test-utils';
+
/**
* Internal dependencies
*/
@@ -14,7 +27,53 @@ export async function setTemplateMode( mode ) {
const saveButtonSelector = '.amp-settings-nav button[type="submit"]';
await page.waitForSelector( saveButtonSelector );
- await scrollToElement( { selector: saveButtonSelector, click: true } );
- await page.waitForResponse( ( response ) => response.url().includes( '/wp-json/amp/v1/options' ) );
+ await Promise.all( [
+ scrollToElement( { selector: saveButtonSelector, click: true } ),
+ page.waitForResponse( ( response ) => response.url().includes( '/wp-json/amp/v1/options' ), { timeout: 10000 } ),
+ ] );
+}
+
+export async function isPluginInstalled( slug, settings ) {
+ await switchUserToAdmin();
+ await visitAdminPage( 'plugins.php' );
+ await page.waitForSelector( 'h1', { text: 'Plugins' } );
+
+ const found = await page.$( `tr${ settings?.checkIsActivated ? '.active' : '' }[data-slug="${ slug }"]` );
+
+ await switchUserToTest();
+
+ return Boolean( found );
+}
+
+export function isPluginActivated( slug ) {
+ return isPluginInstalled( slug, { checkIsActivated: true } );
+}
+
+export async function installPlugin( slug ) {
+ if ( ! await isPluginInstalled( slug ) ) {
+ await _installPlugin( slug );
+ }
+}
+
+export async function activatePlugin( slug ) {
+ await installPlugin( slug );
+
+ if ( ! await isPluginActivated( slug ) ) {
+ await _activatePlugin( slug );
+ }
+}
+
+export async function deactivatePlugin( slug ) {
+ if ( await isPluginActivated( slug ) ) {
+ await _deactivatePlugin( slug );
+ }
+}
+
+export async function uninstallPlugin( slug ) {
+ await deactivatePlugin( slug );
+
+ if ( await isPluginInstalled( slug ) ) {
+ await _uninstallPlugin( slug );
+ }
}
diff --git a/tests/e2e/utils/site-scan-utils.js b/tests/e2e/utils/site-scan-utils.js
index a96b9ab3402..f75b3ef0382 100644
--- a/tests/e2e/utils/site-scan-utils.js
+++ b/tests/e2e/utils/site-scan-utils.js
@@ -1,18 +1,25 @@
export async function testSiteScanning( { statusElementClassName, isAmpFirst } ) {
- const statusTextRegex = /^Scanning \d+\/([\d]+) URLs/;
+ const statusTextRegex = /^Scanning ([\d])+\/([\d]+) URLs/;
const statusText = await page.$eval( `.${ statusElementClassName }`, ( el ) => el.innerText );
expect( statusText ).toMatch( statusTextRegex );
- const scannableUrlsCount = Number( statusText.match( statusTextRegex )[ 1 ] );
+ const currentlyScannedIndex = Number( statusText.match( statusTextRegex )[ 1 ] ) - 1;
+ const scannableUrlsCount = Number( statusText.match( statusTextRegex )[ 2 ] );
+ const urls = [ ...Array( scannableUrlsCount - currentlyScannedIndex ) ];
+
const expectedParams = [
'amp_validate[nonce]',
'amp_validate[omit_stylesheets]',
'amp_validate[cache_bust]',
].map( encodeURI );
- await Promise.all( [ ...Array( scannableUrlsCount ) ].map( ( url, index ) => ( [
- page.waitForResponse( ( response ) => isAmpFirst === response.url().includes( 'amp-first' ) && expectedParams.every( ( param ) => response.url().includes( param ) ) ),
- page.waitForXPath( `//p[@class='${ statusElementClassName }'][contains(text(), 'Scanning ${ index + 1 }/${ scannableUrlsCount } URLs')]` ),
- ] ) ).flat() );
+ // Use generous timeout since site scan may take a while.
+ const timeout = 20000;
+
+ await Promise.all( [
+ ...urls.map( ( url, index ) => page.waitForXPath( `//p[@class='${ statusElementClassName }'][contains(text(), 'Scanning ${ index + 1 }/${ scannableUrlsCount } URLs')]`, { timeout } ) ),
+ page.waitForResponse( ( response ) => isAmpFirst === response.url().includes( 'amp-first' ) && expectedParams.every( ( param ) => response.url().includes( param ) ), { timeout } ),
+ page.waitForXPath( `//p[@class='${ statusElementClassName }'][contains(text(), 'Scan complete')]`, { timeout } ),
+ ] );
}
From 498506402ed7f825d0696ad48a14fef09e5595d4 Mon Sep 17 00:00:00 2001
From: Piotr Delawski
Date: Wed, 20 Oct 2021 16:59:31 +0200
Subject: [PATCH 077/120] Use Twenty Twenty as default theme in Site Scan
---
tests/e2e/specs/admin/site-scan-panel.js | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/tests/e2e/specs/admin/site-scan-panel.js b/tests/e2e/specs/admin/site-scan-panel.js
index 3ed62be9bc7..9eb068dc201 100644
--- a/tests/e2e/specs/admin/site-scan-panel.js
+++ b/tests/e2e/specs/admin/site-scan-panel.js
@@ -23,14 +23,13 @@ import { testSiteScanning } from '../../utils/site-scan-utils';
describe( 'AMP settings screen Site Scan panel', () => {
beforeAll( async () => {
- await installTheme( 'twentytwentyone' );
await installTheme( 'hestia' );
await installPlugin( 'autoptimize' );
} );
afterAll( async () => {
await cleanUpSettings();
- await deleteTheme( 'hestia', { newThemeSlug: 'twentytwentyone' } );
+ await deleteTheme( 'hestia', { newThemeSlug: 'twentytwenty' } );
await uninstallPlugin( 'autoptimize' );
} );
@@ -59,7 +58,7 @@ describe( 'AMP settings screen Site Scan panel', () => {
}
it( 'does not list issues if an AMP compatible theme is activated', async () => {
- await activateTheme( 'twentytwentyone' );
+ await activateTheme( 'twentytwenty' );
await visitAdminPage( 'admin.php', 'page=amp-options' );
@@ -90,7 +89,7 @@ describe( 'AMP settings screen Site Scan panel', () => {
} );
it( 'lists Autoptimize plugin as causing AMP incompatibility', async () => {
- await activateTheme( 'twentytwentyone' );
+ await activateTheme( 'twentytwenty' );
await activatePlugin( 'autoptimize' );
await visitAdminPage( 'admin.php', 'page=amp-options' );
From 54ce73c9db914aabc1151bff20813016ae31581d Mon Sep 17 00:00:00 2001
From: Piotr Delawski
Date: Wed, 20 Oct 2021 18:06:02 +0200
Subject: [PATCH 078/120] Ensure wizard button tests are async functions
---
tests/e2e/specs/amp-onboarding/reader-themes.js | 6 +++---
.../amp-onboarding/technical-background.js | 6 +++---
tests/e2e/specs/amp-onboarding/template-mode.js | 6 +++---
tests/e2e/specs/amp-onboarding/welcome.js | 4 ++--
tests/e2e/utils/onboarding-wizard-utils.js | 17 ++++++++---------
5 files changed, 19 insertions(+), 20 deletions(-)
diff --git a/tests/e2e/specs/amp-onboarding/reader-themes.js b/tests/e2e/specs/amp-onboarding/reader-themes.js
index c32b96810a2..64245f8f3bc 100644
--- a/tests/e2e/specs/amp-onboarding/reader-themes.js
+++ b/tests/e2e/specs/amp-onboarding/reader-themes.js
@@ -20,8 +20,8 @@ describe( 'Reader themes', () => {
expect( itemCount ).toBe( 11 );
await expect( page ).not.toMatchElement( 'input[type="radio"]:checked' );
- testNextButton( { text: 'Next', disabled: true } );
- testPreviousButton( { text: 'Previous' } );
+ await testNextButton( { text: 'Next', disabled: true } );
+ await testPreviousButton( { text: 'Previous' } );
} );
it( 'should allow different themes to be selected', async () => {
@@ -34,7 +34,7 @@ describe( 'Reader themes', () => {
await selectReaderTheme( 'twentysixteen' );
await expect( page ).toMatchElement( '.selectable--selected h4', { text: 'Twenty Sixteen' } );
- testNextButton( { text: 'Next' } );
+ await testNextButton( { text: 'Next' } );
} );
} );
diff --git a/tests/e2e/specs/amp-onboarding/technical-background.js b/tests/e2e/specs/amp-onboarding/technical-background.js
index 6115b0d7869..a842a9c04a2 100644
--- a/tests/e2e/specs/amp-onboarding/technical-background.js
+++ b/tests/e2e/specs/amp-onboarding/technical-background.js
@@ -11,8 +11,8 @@ describe( 'Technical background', () => {
await expect( page ).toMatchElement( 'p', { text: /^To recommend/ } );
- testNextButton( { text: 'Next', disabled: true } );
- testPreviousButton( { text: 'Previous' } );
+ await testNextButton( { text: 'Next', disabled: true } );
+ await testPreviousButton( { text: 'Previous' } );
} );
it( 'should show two options, none checked', async () => {
@@ -32,6 +32,6 @@ describe( 'Technical background', () => {
await expect( page ).toClick( 'label', { text: /Non-technical/ } );
await expect( page ).toMatchElement( '.selectable--selected h2', { text: 'Non-technical or wanting a simpler setup' } );
- testNextButton( { text: 'Next', disabled: false } );
+ await testNextButton( { text: 'Next', disabled: false } );
} );
} );
diff --git a/tests/e2e/specs/amp-onboarding/template-mode.js b/tests/e2e/specs/amp-onboarding/template-mode.js
index da5447e8343..3a06c06ab67 100644
--- a/tests/e2e/specs/amp-onboarding/template-mode.js
+++ b/tests/e2e/specs/amp-onboarding/template-mode.js
@@ -26,8 +26,8 @@ describe( 'Template mode', () => {
await expect( page ).not.toMatchElement( 'input[type="radio"]:checked' );
- testNextButton( { text: 'Next', disabled: true } );
- testPreviousButton( { text: 'Previous' } );
+ await testNextButton( { text: 'Next', disabled: true } );
+ await testPreviousButton( { text: 'Previous' } );
} );
it( 'should allow options to be selected', async () => {
@@ -40,7 +40,7 @@ describe( 'Template mode', () => {
await clickMode( 'reader' );
await expect( page ).toMatchElement( '.selectable--selected h2', { text: 'Reader' } );
- testNextButton( { text: 'Next' } );
+ await testNextButton( { text: 'Next' } );
} );
} );
diff --git a/tests/e2e/specs/amp-onboarding/welcome.js b/tests/e2e/specs/amp-onboarding/welcome.js
index bf865610d10..f62c8fc2e0e 100644
--- a/tests/e2e/specs/amp-onboarding/welcome.js
+++ b/tests/e2e/specs/amp-onboarding/welcome.js
@@ -17,7 +17,7 @@ describe( 'welcome', () => {
it( 'should contain content', async () => {
await expect( page ).toMatchElement( '.welcome' );
- testPreviousButton( { exists: false } );
- testNextButton( { text: 'Next' } );
+ await testPreviousButton( { exists: false } );
+ await testNextButton( { text: 'Next' } );
} );
} );
diff --git a/tests/e2e/utils/onboarding-wizard-utils.js b/tests/e2e/utils/onboarding-wizard-utils.js
index 343148ddbe8..7efc4d7b047 100644
--- a/tests/e2e/utils/onboarding-wizard-utils.js
+++ b/tests/e2e/utils/onboarding-wizard-utils.js
@@ -110,20 +110,20 @@ export async function testCloseButton( { exists = true } ) {
}
}
-export async function testPreviousButton( { exists = true, disabled = false } ) {
+export async function testPreviousButton( { exists = true, text = 'Previous', disabled = false } ) {
if ( exists ) {
- await expect( page ).toMatchElement( `button${ disabled ? '[disabled]' : '' }`, { text: 'Previous' } );
+ await expect( page ).toMatchElement( `button${ disabled ? '[disabled]' : '' }`, { text } );
} else {
- await expect( page ).not.toMatchElement( `button${ disabled ? '[disabled]' : '' }`, { text: 'Previous' } );
+ await expect( page ).not.toMatchElement( `button${ disabled ? '[disabled]' : '' }`, { text } );
}
}
-export function testNextButton( { element = 'button', text, disabled = false } ) {
- expect( page ).toMatchElement( `${ element }${ disabled ? '[disabled]' : '' }`, { text } );
+export async function testNextButton( { element = 'button', text = 'Next', disabled = false } ) {
+ await expect( page ).toMatchElement( `${ element }${ disabled ? '[disabled]' : '' }`, { text } );
}
-export function testTitle( { text, element = 'h1' } ) {
- expect( page ).toMatchElement( element, { text } );
+export async function testTitle( { text, element = 'h1' } ) {
+ await expect( page ).toMatchElement( element, { text } );
}
/**
@@ -144,7 +144,6 @@ export async function cleanUpSettings() {
theme_support: 'reader',
plugin_configured: false,
} } ),
- ],
- );
+ ] );
} );
}
From b3c00ea3aac08fd337230800e9f486e051c7699c Mon Sep 17 00:00:00 2001
From: Piotr Delawski
Date: Wed, 20 Oct 2021 18:45:34 +0200
Subject: [PATCH 079/120] Add E2E tests covering Site Scan summary in
Onboarding Wizard
---
.../site-scan-results/plugins-with-issues.js | 9 ++-
.../site-scan-results/themes-with-issues.js | 9 ++-
tests/e2e/specs/amp-onboarding/site-scan.js | 74 +++++++++++++++----
3 files changed, 73 insertions(+), 19 deletions(-)
diff --git a/assets/src/components/site-scan-results/plugins-with-issues.js b/assets/src/components/site-scan-results/plugins-with-issues.js
index fd3c0ac44cf..9ff54bbfb27 100644
--- a/assets/src/components/site-scan-results/plugins-with-issues.js
+++ b/assets/src/components/site-scan-results/plugins-with-issues.js
@@ -2,6 +2,7 @@
* External dependencies
*/
import PropTypes from 'prop-types';
+import classnames from 'classnames';
/**
* WordPress dependencies
@@ -19,10 +20,11 @@ import { SiteScanResults } from './index';
* Render a list of plugins that cause issues.
*
* @param {Object} props Component props.
+ * @param {string} props.className Component class name.
* @param {Array} props.issues List of plugins issues.
- * @param {Array} props.validatedUrlsLink URL to the Validated URLs page.
+ * @param {string} props.validatedUrlsLink URL to the Validated URLs page.
*/
-export function PluginsWithIssues( { issues = [], validatedUrlsLink, ...props } ) {
+export function PluginsWithIssues( { issues = [], validatedUrlsLink, className, ...props } ) {
const pluginsData = useNormalizedPluginsData();
const sources = issues?.map( ( slug ) => pluginsData?.[ slug ] ?? { name: slug } ) || [];
@@ -33,13 +35,14 @@ export function PluginsWithIssues( { issues = [], validatedUrlsLink, ...props }
count={ issues.length }
sources={ sources }
validatedUrlsLink={ validatedUrlsLink }
- className="site-scan-results--plugins"
+ className={ classnames( 'site-scan-results--plugins', className ) }
{ ...props }
/>
);
}
PluginsWithIssues.propTypes = {
+ className: PropTypes.string,
issues: PropTypes.array.isRequired,
validatedUrlsLink: PropTypes.string,
};
diff --git a/assets/src/components/site-scan-results/themes-with-issues.js b/assets/src/components/site-scan-results/themes-with-issues.js
index 748831e6d2e..7cb1324de9a 100644
--- a/assets/src/components/site-scan-results/themes-with-issues.js
+++ b/assets/src/components/site-scan-results/themes-with-issues.js
@@ -2,6 +2,7 @@
* External dependencies
*/
import PropTypes from 'prop-types';
+import classnames from 'classnames';
/**
* WordPress dependencies
@@ -19,10 +20,11 @@ import { SiteScanResults } from './index';
* Renders a list of themes that cause issues.
*
* @param {Object} props Component props.
+ * @param {string} props.className Component class name.
* @param {Array} props.issues List of theme issues.
- * @param {Array} props.validatedUrlsLink URL to the Validated URLs page.
+ * @param {string} props.validatedUrlsLink URL to the Validated URLs page.
*/
-export function ThemesWithIssues( { issues = [], validatedUrlsLink, ...props } ) {
+export function ThemesWithIssues( { issues = [], validatedUrlsLink, className, ...props } ) {
const themesData = useNormalizedThemesData();
const sources = issues?.map( ( slug ) => themesData?.[ slug ] ?? { name: slug } ) || [];
@@ -33,13 +35,14 @@ export function ThemesWithIssues( { issues = [], validatedUrlsLink, ...props } )
count={ issues.length }
sources={ sources }
validatedUrlsLink={ validatedUrlsLink }
- className="site-scan-results--themes"
+ className={ classnames( 'site-scan-results--themes', className ) }
{ ...props }
/>
);
}
ThemesWithIssues.propTypes = {
+ className: PropTypes.string,
issues: PropTypes.array.isRequired,
validatedUrlsLink: PropTypes.string,
};
diff --git a/tests/e2e/specs/amp-onboarding/site-scan.js b/tests/e2e/specs/amp-onboarding/site-scan.js
index 356c6993346..c3cc7f260cc 100644
--- a/tests/e2e/specs/amp-onboarding/site-scan.js
+++ b/tests/e2e/specs/amp-onboarding/site-scan.js
@@ -1,3 +1,12 @@
+/**
+ * WordPress dependencies
+ */
+import {
+ activateTheme,
+ deleteTheme,
+ installTheme,
+} from '@wordpress/e2e-test-utils';
+
/**
* Internal dependencies
*/
@@ -7,33 +16,72 @@ import {
testPreviousButton,
} from '../../utils/onboarding-wizard-utils';
import { testSiteScanning } from '../../utils/site-scan-utils';
+import {
+ activatePlugin,
+ deactivatePlugin,
+ installPlugin,
+ uninstallPlugin,
+} from '../../utils/amp-settings-utils';
describe( 'Site Scan', () => {
- beforeEach( async () => {
- await moveToSiteScanScreen( { technical: true } );
+ beforeAll( async () => {
+ await installTheme( 'hestia' );
+ await installPlugin( 'autoptimize' );
+ } );
+
+ afterAll( async () => {
+ await deleteTheme( 'hestia', { newThemeSlug: 'twentytwenty' } );
+ await uninstallPlugin( 'autoptimize' );
} );
it( 'should start a site scan immediately', async () => {
- await page.waitForSelector( '.amp-onboarding-wizard-panel h1' );
+ await moveToSiteScanScreen( { technical: true } );
- const screenHeading = await page.$eval( '.amp-onboarding-wizard-panel h1', ( el ) => el.innerText );
- expect( screenHeading ).toContain( 'Site Scan' );
+ await Promise.all( [
+ expect( page ).toMatchElement( '.amp-onboarding-wizard-panel h1', { text: 'Site Scan' } ),
+ expect( page ).toMatchElement( '.site-scan__heading', { text: 'Please wait a minute' } ),
+ testNextButton( { text: 'Next', disabled: true } ),
+ testPreviousButton( { text: 'Previous' } ),
+ testSiteScanning( {
+ statusElementClassName: 'site-scan__status',
+ isAmpFirst: true,
+ } ),
+ ] );
- const scanInProgressHandle = await page.waitForXPath( `//p[contains(text(), 'Please wait a minute')]` );
- expect( scanInProgressHandle ).not.toBeNull();
+ await testNextButton( { text: 'Next' } );
+ await testPreviousButton( { text: 'Previous' } );
- testNextButton( { text: 'Next', disabled: true } );
- testPreviousButton( { text: 'Previous' } );
+ await expect( page ).toMatchElement( '.site-scan__heading', { text: 'Scan complete', timeout: 10000 } );
+ await expect( page ).toMatchElement( '.site-scan__section p', { text: /Site scan found no issues/ } );
+ } );
+
+ it( 'should list out plugin and theme issues after the scan', async () => {
+ await activateTheme( 'hestia' );
+ await activatePlugin( 'autoptimize' );
+
+ await moveToSiteScanScreen( { technical: true } );
await testSiteScanning( {
statusElementClassName: 'site-scan__status',
isAmpFirst: true,
} );
- const scanCompleteHandle = await page.waitForXPath( `//p[@class='site-scan__heading'][contains(text(), 'Scan complete')]` );
- expect( scanCompleteHandle ).not.toBeNull();
+ await expect( page ).toMatchElement( '.site-scan__heading', { text: 'Scan complete', timeout: 10000 } );
+ await expect( page ).toMatchElement( '.site-scan__section p', { text: /Site scan found issues/ } );
+
+ await expect( page ).toMatchElement( '.site-scan-results--themes' );
+ await expect( page ).toMatchElement( '.site-scan-results--plugins' );
+
+ const totalIssuesCount = await page.$$eval( '.site-scan-results__source', ( sources ) => sources.length );
+ expect( totalIssuesCount ).toBe( 2 );
+
+ await expect( page ).toMatchElement( '.site-scan-results--themes .site-scan-results__source-name', { text: /Hestia/ } );
+ await expect( page ).toMatchElement( '.site-scan-results--plugins .site-scan-results__source-name', { text: /Autoptimize/ } );
+
+ await testNextButton( { text: 'Next' } );
+ await testPreviousButton( { text: 'Previous' } );
- testNextButton( { text: 'Next' } );
- testPreviousButton( { text: 'Previous' } );
+ await deactivatePlugin( 'autoptimize' );
+ await activateTheme( 'twentytwenty' );
} );
} );
From 220fd8c85d0982953f0c21dead4797b03e1e65bd Mon Sep 17 00:00:00 2001
From: Piotr Delawski
Date: Fri, 22 Oct 2021 14:01:33 +0200
Subject: [PATCH 080/120] Add notices for inactive and uninstalled sources
---
assets/src/components/amp-notice/style.css | 4 +
.../test/use-normalized-plugins-data.js | 43 ++-------
.../use-normalized-plugins-data.js | 19 ++--
.../site-scan-results/plugins-with-issues.js | 27 ++++--
.../site-scan-results/site-scan-results.js | 12 +--
.../site-scan-sources-list.js | 94 +++++++++++++++++++
.../site-scan-results/sources-list.js | 54 -----------
.../components/site-scan-results/style.scss | 8 +-
.../site-scan-results/themes-with-issues.js | 25 +++--
.../test/use-normalized-themes-data.js | 43 ++-------
.../use-normalized-themes-data.js | 15 +--
tests/e2e/specs/admin/site-scan-panel.js | 30 ++++++
12 files changed, 197 insertions(+), 177 deletions(-)
create mode 100644 assets/src/components/site-scan-results/site-scan-sources-list.js
delete mode 100644 assets/src/components/site-scan-results/sources-list.js
diff --git a/assets/src/components/amp-notice/style.css b/assets/src/components/amp-notice/style.css
index d9f1d4f1033..8bd713ae4e7 100644
--- a/assets/src/components/amp-notice/style.css
+++ b/assets/src/components/amp-notice/style.css
@@ -52,6 +52,10 @@
background-color: #ffefef;
}
+.amp-notice.amp-notice--plain {
+ padding: 1px 5px;
+}
+
.amp-notice--small {
font-size: 14px;
line-height: 1.5;
diff --git a/assets/src/components/plugins-context-provider/test/use-normalized-plugins-data.js b/assets/src/components/plugins-context-provider/test/use-normalized-plugins-data.js
index 08950bb4252..0f1b561acfe 100644
--- a/assets/src/components/plugins-context-provider/test/use-normalized-plugins-data.js
+++ b/assets/src/components/plugins-context-provider/test/use-normalized-plugins-data.js
@@ -20,13 +20,10 @@ jest.mock( '../index' );
let returnValue = {};
-function ComponentContainingHook( { skipInactive } ) {
- returnValue = useNormalizedPluginsData( { skipInactive } );
+function ComponentContainingHook() {
+ returnValue = useNormalizedPluginsData();
return null;
}
-ComponentContainingHook.propTypes = {
- skipInactive: PropTypes.bool,
-};
const Providers = ( { children, fetchingPlugins, plugins = [] } ) => (
@@ -105,19 +102,20 @@ describe( 'useNormalizedPluginsData', () => {
},
] }
>
-
+
,
container,
);
} );
- expect( returnValue ).toMatchObject( {
+ expect( returnValue ).toStrictEqual( {
'acme-inc': {
author: 'Acme Inc.',
author_uri: 'http://example.com',
name: 'Acme Plugin',
plugin: 'acme-inc',
status: 'inactive',
+ slug: 'acme-inc',
version: '1.0.1',
},
amp: {
@@ -126,38 +124,9 @@ describe( 'useNormalizedPluginsData', () => {
name: 'AMP',
plugin: 'amp/amp',
status: 'active',
+ slug: 'amp',
version: '2.2.0-alpha',
},
} );
} );
-
- it( 'skips inactive plugins', () => {
- act( () => {
- render(
-
-
- ,
- container,
- );
- } );
-
- expect( returnValue ).toMatchObject( {
- amp: {
- plugin: 'amp/amp',
- status: 'active',
- },
- } );
- } );
} );
diff --git a/assets/src/components/plugins-context-provider/use-normalized-plugins-data.js b/assets/src/components/plugins-context-provider/use-normalized-plugins-data.js
index 3663e3aca9c..1f08a085f81 100644
--- a/assets/src/components/plugins-context-provider/use-normalized-plugins-data.js
+++ b/assets/src/components/plugins-context-provider/use-normalized-plugins-data.js
@@ -8,7 +8,7 @@ import { useContext, useEffect, useState } from '@wordpress/element';
*/
import { Plugins } from './index';
-export function useNormalizedPluginsData( { skipInactive = true } = {} ) {
+export function useNormalizedPluginsData() {
const { fetchingPlugins, plugins } = useContext( Plugins );
const [ normalizedPluginsData, setNormalizedPluginsData ] = useState( [] );
@@ -17,29 +17,24 @@ export function useNormalizedPluginsData( { skipInactive = true } = {} ) {
return;
}
- setNormalizedPluginsData( () => plugins.reduce( ( acc, source ) => {
- const { status, plugin } = source;
+ setNormalizedPluginsData( plugins.reduce( ( acc, source ) => {
+ const slug = source?.plugin?.match( /^(?:[^\/]*\/)?(.*?)$/ )?.[ 1 ];
- if ( skipInactive && status !== 'active' ) {
- return acc;
- }
-
- const pluginSlug = plugin.match( /^(?:[^\/]*\/)?(.*?)$/ )[ 1 ];
-
- if ( ! pluginSlug ) {
+ if ( ! slug ) {
return acc;
}
return {
...acc,
- [ pluginSlug ]: Object.keys( source ).reduce( ( props, key ) => ( {
+ [ slug ]: Object.keys( source ).reduce( ( props, key ) => ( {
...props,
+ slug,
// Flatten every prop that contains a `raw` member.
[ key ]: source[ key ]?.raw ?? source[ key ],
} ), {} ),
};
}, {} ) );
- }, [ fetchingPlugins, plugins, skipInactive ] );
+ }, [ fetchingPlugins, plugins ] );
return normalizedPluginsData;
}
diff --git a/assets/src/components/site-scan-results/plugins-with-issues.js b/assets/src/components/site-scan-results/plugins-with-issues.js
index 9ff54bbfb27..85976222768 100644
--- a/assets/src/components/site-scan-results/plugins-with-issues.js
+++ b/assets/src/components/site-scan-results/plugins-with-issues.js
@@ -8,41 +8,48 @@ import classnames from 'classnames';
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
+import { useMemo } from '@wordpress/element';
/**
* Internal dependencies
*/
import { useNormalizedPluginsData } from '../plugins-context-provider/use-normalized-plugins-data';
import { IconLaptopPlug } from '../svg/laptop-plug';
+import { SiteScanSourcesList } from './site-scan-sources-list';
import { SiteScanResults } from './index';
/**
* Render a list of plugins that cause issues.
*
- * @param {Object} props Component props.
- * @param {string} props.className Component class name.
- * @param {Array} props.issues List of plugins issues.
- * @param {string} props.validatedUrlsLink URL to the Validated URLs page.
+ * @param {Object} props Component props.
+ * @param {string} props.className Component class name.
+ * @param {Array} props.issues List of plugins issues.
*/
-export function PluginsWithIssues( { issues = [], validatedUrlsLink, className, ...props } ) {
+export function PluginsWithIssues( { issues = [], className, ...props } ) {
const pluginsData = useNormalizedPluginsData();
- const sources = issues?.map( ( slug ) => pluginsData?.[ slug ] ?? { name: slug } ) || [];
+ const sources = useMemo( () => issues?.map( ( slug ) => pluginsData?.[ slug ] ?? {
+ slug,
+ status: 'uninstalled',
+ } ) || [], [ issues, pluginsData ] );
return (
}
count={ issues.length }
- sources={ sources }
- validatedUrlsLink={ validatedUrlsLink }
className={ classnames( 'site-scan-results--plugins', className ) }
{ ...props }
- />
+ >
+
+
);
}
PluginsWithIssues.propTypes = {
className: PropTypes.string,
issues: PropTypes.array.isRequired,
- validatedUrlsLink: PropTypes.string,
};
diff --git a/assets/src/components/site-scan-results/site-scan-results.js b/assets/src/components/site-scan-results/site-scan-results.js
index 7a65a5cead3..407e7502324 100644
--- a/assets/src/components/site-scan-results/site-scan-results.js
+++ b/assets/src/components/site-scan-results/site-scan-results.js
@@ -14,26 +14,24 @@ import PropTypes from 'prop-types';
* Internal dependencies
*/
import './style.scss';
-import { Loading } from '../loading';
import { Selectable } from '../selectable';
-import { SourcesList } from './sources-list';
/**
* Renders a panel with a site scan results.
*
* @param {Object} props Component props.
+ * @param {Object} props.children Component children.
* @param {number} props.count Issues count.
* @param {string} props.className Additional class names.
* @param {Element} props.icon Panel icon.
- * @param {Array} props.sources Array of issues sources data.
* @param {string} props.title Panel title.
* @param {string} props.validatedUrlsLink URL to the Validated URLs page.
*/
export function SiteScanResults( {
+ children,
count,
className,
icon,
- sources,
title,
validatedUrlsLink,
} ) {
@@ -52,9 +50,7 @@ export function SiteScanResults( {