From 5daeeed5032654071cd7859bfe998866f6229081 Mon Sep 17 00:00:00 2001 From: Mehul Gohil Date: Sun, 29 Jan 2023 08:33:20 +0530 Subject: [PATCH 01/22] update label for required fields --- includes/datatypes.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/includes/datatypes.php b/includes/datatypes.php index 83150251..e6128dd7 100644 --- a/includes/datatypes.php +++ b/includes/datatypes.php @@ -524,7 +524,7 @@ function get_meta_fields() { ), array( 'slug' => 'podcasting_talent_name', - 'title' => __( 'Artist / Author name', 'simple-podcasting' ), + 'title' => __( 'Artist / Author name (required)', 'simple-podcasting' ), 'type' => 'textfield', ), array( @@ -534,7 +534,7 @@ function get_meta_fields() { ), array( 'slug' => 'podcasting_summary', - 'title' => __( 'Summary', 'simple-podcasting' ), + 'title' => __( 'Summary (required)', 'simple-podcasting' ), 'type' => 'textarea', ), array( @@ -560,7 +560,7 @@ function get_meta_fields() { ), array( 'slug' => 'podcasting_image', - 'title' => __( 'Cover image', 'simple-podcasting' ), + 'title' => __( 'Cover image (required)', 'simple-podcasting' ), 'type' => 'image', 'description' => __( 'Minimum size: 1400px x 1400 px — maximum size: 2048px x 2048px', 'simple-podcasting' ), ), From 6235cc6719da838a0b40cf4f393922f648bbc85e Mon Sep 17 00:00:00 2001 From: Mehul Gohil Date: Tue, 31 Jan 2023 16:47:02 +0530 Subject: [PATCH 02/22] add validation for required podcasts field --- includes/datatypes.php | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/includes/datatypes.php b/includes/datatypes.php index e6128dd7..4f101734 100644 --- a/includes/datatypes.php +++ b/includes/datatypes.php @@ -848,3 +848,37 @@ function get_podcasting_language_options() { ) ); } + +/** + * Validate Podcast Taxonomy Fields. + * + * @param string $term + * @param [type] $taxonomy + * @param [type] $args + * @return void + */ +function validate_taxonomy_fields( $term, $taxonomy, $args ) { + // Bailout, if not the podcasts taxonomy. + if ( 'podcasting_podcasts' !== $taxonomy ) { + return; + } + + // Validate Empty Podcast Author Name. + if ( empty( trim( $args['podcasting_talent_name'] ) ) ) { + return new \WP_Error( 'empty_term_talent_name', __( 'A Podcast Artist / Author Name is required.' ) ); + } + + // Validate Empty Podcast Summary. + if ( empty( trim( $args['podcasting_summary'] ) ) ) { + return new \WP_Error( 'empty_term_summary', __( 'A Podcast summary is required.' ) ); + } + + // Validate Podcast Image. + if ( empty( trim( $args['podcasting_image'] ) ) ) { + return new \WP_Error( 'empty_term_cover_image', __( 'A Podcast cover image is required.' ) ); + } + + +} + +add_filter( 'pre_insert_term', __NAMESPACE__ . '\validate_taxonomy_fields', 10, 3 ); From 0541a6f9b4f63c61381f03313ff7cbdf25a206ea Mon Sep 17 00:00:00 2001 From: Mehul Gohil Date: Tue, 31 Jan 2023 16:56:50 +0530 Subject: [PATCH 03/22] improve validation for podcast fields --- includes/datatypes.php | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/includes/datatypes.php b/includes/datatypes.php index 4f101734..669b8af9 100644 --- a/includes/datatypes.php +++ b/includes/datatypes.php @@ -852,33 +852,38 @@ function get_podcasting_language_options() { /** * Validate Podcast Taxonomy Fields. * - * @param string $term - * @param [type] $taxonomy - * @param [type] $args - * @return void + * @param string $term Term. + * @param string $taxonomy Taxonomy Name. + * @param array $args List of arguments. + * + * @return string */ function validate_taxonomy_fields( $term, $taxonomy, $args ) { // Bailout, if not the podcasts taxonomy. if ( 'podcasting_podcasts' !== $taxonomy ) { - return; + return $term; + } + + if ( empty( trim( $args['tag-name'] ) ) ) { + return new \WP_Error( 'empty_term_name', __( 'A podcast name is required..' ) ); } // Validate Empty Podcast Author Name. if ( empty( trim( $args['podcasting_talent_name'] ) ) ) { - return new \WP_Error( 'empty_term_talent_name', __( 'A Podcast Artist / Author Name is required.' ) ); + return new \WP_Error( 'empty_term_talent_name', __( 'A podcast artist or author name is required.' ) ); } // Validate Empty Podcast Summary. if ( empty( trim( $args['podcasting_summary'] ) ) ) { - return new \WP_Error( 'empty_term_summary', __( 'A Podcast summary is required.' ) ); + return new \WP_Error( 'empty_term_summary', __( 'A podcast summary is required.' ) ); } // Validate Podcast Image. if ( empty( trim( $args['podcasting_image'] ) ) ) { - return new \WP_Error( 'empty_term_cover_image', __( 'A Podcast cover image is required.' ) ); + return new \WP_Error( 'empty_term_cover_image', __( 'A podcast cover image is required.' ) ); } - + return $term; } add_filter( 'pre_insert_term', __NAMESPACE__ . '\validate_taxonomy_fields', 10, 3 ); From 4d45ce21e568a3dc4632dd62550663d8defe102f Mon Sep 17 00:00:00 2001 From: Nate Conley Date: Fri, 3 Mar 2023 16:43:28 -1000 Subject: [PATCH 04/22] Validate onboarding and e2e tests --- includes/admin/onboarding.php | 43 ++++++++----------- includes/admin/views/onboarding-page-one.php | 15 +++++-- includes/datatypes.php | 2 +- tests/cypress/fixtures/example.jpg | Bin 0 -> 36287 bytes tests/cypress/integration/block.test.js | 13 +++++- tests/cypress/integration/onboarding.test.js | 13 ++++-- tests/cypress/integration/taxonomy.test.js | 19 +++++++- 7 files changed, 68 insertions(+), 37 deletions(-) create mode 100644 tests/cypress/fixtures/example.jpg diff --git a/includes/admin/onboarding.php b/includes/admin/onboarding.php index f66d1881..2887750a 100644 --- a/includes/admin/onboarding.php +++ b/includes/admin/onboarding.php @@ -64,35 +64,21 @@ function onboarding_action_handler() { return; } - $podcast_name = isset( $_POST['podcast-name'] ) ? sanitize_text_field( wp_unslash( $_POST['podcast-name'] ) ) : null; - $podcast_description = isset( $_POST['podcast-description'] ) ? sanitize_text_field( wp_unslash( $_POST['podcast-description'] ) ) : null; - $podcast_category = isset( $_POST['podcast-category'] ) ? sanitize_text_field( wp_unslash( $_POST['podcast-category'] ) ) : null; - $podcast_cover_id = isset( $_POST['podcast-cover-image-id'] ) ? absint( wp_unslash( $_POST['podcast-cover-image-id'] ) ) : null; - - if ( empty( $podcast_name ) || empty( $podcast_category ) ) { - add_action( - 'admin_notices', - function() use ( $podcast_name, $podcast_category ) { - ?> -
- -

- - - -

- -
- $podcasting_talent_name, + 'podcasting_summary' => $podcast_description, + 'podcasting_image' => $podcast_cover_id, + ] ); if ( is_wp_error( $result ) ) { @@ -109,6 +95,11 @@ function() use ( $result ) { return; } + /** Add podcast author. */ + if ( $podcasting_talent_name ) { + update_term_meta( $result['term_id'], 'podcasting_talent_name', $podcasting_talent_name ); + } + /** Add podcast summary. */ if ( $podcast_description ) { update_term_meta( $result['term_id'], 'podcasting_summary', $podcast_description ); diff --git a/includes/admin/views/onboarding-page-one.php b/includes/admin/views/onboarding-page-one.php index ea3affea..6081eff0 100644 --- a/includes/admin/views/onboarding-page-one.php +++ b/includes/admin/views/onboarding-page-one.php @@ -19,17 +19,24 @@
+ +
+ +
+
+
+
- -
+ +
- - + +
diff --git a/includes/datatypes.php b/includes/datatypes.php index 669b8af9..f713768d 100644 --- a/includes/datatypes.php +++ b/includes/datatypes.php @@ -864,7 +864,7 @@ function validate_taxonomy_fields( $term, $taxonomy, $args ) { return $term; } - if ( empty( trim( $args['tag-name'] ) ) ) { + if ( empty( trim( $term ) ) ) { return new \WP_Error( 'empty_term_name', __( 'A podcast name is required..' ) ); } diff --git a/tests/cypress/fixtures/example.jpg b/tests/cypress/fixtures/example.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b0f2052e045a4826ed450e1ed2849e6e40e925d9 GIT binary patch literal 36287 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 JbAN9~|Jli_$}Ru^ literal 0 HcmV?d00001 diff --git a/tests/cypress/integration/block.test.js b/tests/cypress/integration/block.test.js index 6976fdf9..f6986856 100644 --- a/tests/cypress/integration/block.test.js +++ b/tests/cypress/integration/block.test.js @@ -4,8 +4,17 @@ describe('Admin can publish posts with podcast block', () => { if (Cypress.env('HAS_BLOCK_EDITOR')) { it('Can insert the block and publish the post', () => { cy.login(); - - cy.createTerm(taxonomy, 'podcasting_podcasts'); + cy.uploadMedia('tests/cypress/fixtures/example.jpg'); + cy.createTerm(taxonomy, 'podcasting_podcasts', { + beforeSave: () => { + cy.get('#podcasting_talent_name').type('Person Doe'); + cy.get('#podcasting_summary').type('Lorem ipsum dolor'); + cy.get('#image-podcasting_image').click(); + cy.get('#menu-item-browse').click(); + cy.get('.attachments-wrapper').click(); + cy.get('.media-button-select').click(); + }, + }); cy.visit('/wp-admin/post-new.php'); cy.closeWelcomeGuide(); diff --git a/tests/cypress/integration/onboarding.test.js b/tests/cypress/integration/onboarding.test.js index e0f335cf..91a5662a 100644 --- a/tests/cypress/integration/onboarding.test.js +++ b/tests/cypress/integration/onboarding.test.js @@ -6,6 +6,7 @@ describe('Onboarding tests', () => { }); beforeEach(() => { + cy.uploadMedia('tests/cypress/fixtures/example.jpg'); cy.activatePlugin('simple-podcasting'); cy.deleteAllTerms('podcasting_podcasts'); cy.deactivatePlugin('simple-podcasting'); @@ -29,8 +30,7 @@ describe('Onboarding tests', () => { .closest('form') .submit(); - cy.contains('Show name is required.'); - cy.contains('Podcast category is required.'); + cy.contains('Taxonomy error: A podcast name is required..'); }); it('Should pass onboarding', () => { @@ -39,8 +39,15 @@ describe('Onboarding tests', () => { const podcastName = 'Onboarding ' + randomName(); cy.get('input[name=podcast-name]').click().type(podcastName); + cy.get('input[name="podcast-artist"]').type('Person Doe'); + cy.get('textarea[name="podcast-description"]').type( + 'Lorem ipsum dolor' + ); + cy.get('#simple-podcasting__upload-cover-image').click(); + cy.get('#menu-item-browse').click(); + cy.get('.attachments-wrapper').click(); + cy.get('.media-button-select').click(); cy.get('select[name=podcast-category]').select('Arts'); - cy.get('#simple-podcasting__create-podcast-button') .closest('form') .submit(); diff --git a/tests/cypress/integration/taxonomy.test.js b/tests/cypress/integration/taxonomy.test.js index 7ebc1cec..4b537b7c 100644 --- a/tests/cypress/integration/taxonomy.test.js +++ b/tests/cypress/integration/taxonomy.test.js @@ -30,7 +30,17 @@ describe('Admin can create and update podcast taxonomy', () => { }); it('Can add a new taxonomy', () => { - cy.createTerm('Remote work', 'podcasting_podcasts'); + cy.uploadMedia('tests/cypress/fixtures/example.jpg'); + cy.createTerm('Remote work', 'podcasting_podcasts', { + beforeSave: () => { + cy.get('#podcasting_talent_name').type('Person Doe'); + cy.get('#podcasting_summary').type('Lorem ipsum dolor'); + cy.get('#image-podcasting_image').click(); + cy.get('#menu-item-browse').click(); + cy.get('.attachments-wrapper').click(); + cy.get('.media-button-select').click(); + }, + }); cy.get('.row-title').should('have.text', 'Remote work'); }); @@ -76,9 +86,16 @@ describe('Admin can create and update podcast taxonomy', () => { for (const [typeOfShowKey, typeOfShowName] of Object.entries(tests)) { it(`Can add taxonomy with ${typeOfShowName} type of show`, () => { const podcastName = 'Podcast ' + randomName(); + cy.uploadMedia('tests/cypress/fixtures/example.jpg'); cy.createTerm(podcastName, 'podcasting_podcasts', { beforeSave: () => { cy.get('#podcasting_type_of_show').select(typeOfShowName); + cy.get('#podcasting_talent_name').type('Person Doe'); + cy.get('#podcasting_summary').type('Lorem ipsum dolor'); + cy.get('#image-podcasting_image').click(); + cy.get('#menu-item-browse').click(); + cy.get('.attachments-wrapper').click(); + cy.get('.media-button-select').click(); }, }).then((term) => { cy.visit( From 4036df4e1e29fd4f13c50b4614777fbc1d5de36b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20Ivani=C4=87?= Date: Wed, 8 Mar 2023 13:32:41 +0100 Subject: [PATCH 05/22] Add server side validation to onboarding step 1 --- includes/datatypes.php | 51 +++++++++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/includes/datatypes.php b/includes/datatypes.php index f713768d..ace35d44 100644 --- a/includes/datatypes.php +++ b/includes/datatypes.php @@ -864,25 +864,60 @@ function validate_taxonomy_fields( $term, $taxonomy, $args ) { return $term; } - if ( empty( trim( $term ) ) ) { - return new \WP_Error( 'empty_term_name', __( 'A podcast name is required..' ) ); + $referer = sanitize_text_field( $_POST['_wp_http_referer'] ); + $is_onboarding_step_1 = false !== strpos( $referer, 'page=simple-podcasting-onboarding&step=1' ); + + $term = trim( $term ); + if ( ! $is_onboarding_step_1 && empty( $term ) ) { + return new \WP_Error( 'empty_term_name', __( 'A podcast name is required.' ) ); + } + + if ( $is_onboarding_step_1 ) { + $args['tag-name'] = sanitize_title( $_POST['podcast-name'] ); + $args['podcasting_category_1'] = sanitize_text_field( $_POST['podcast-category'] ); + } + + // Require podcast name. + $args['tag-name'] = trim( $args['tag-name'] ); + if ( empty( $args['tag-name'] ) ) { + return new \WP_Error( 'empty_term_name', __( 'A podcast name is required.' ) ); } - // Validate Empty Podcast Author Name. - if ( empty( trim( $args['podcasting_talent_name'] ) ) ) { + // Require podcast author name only on term edit screen. + $args['podcasting_talent_name'] = trim( $args['podcasting_talent_name'] ); + if ( empty( $args['podcasting_talent_name'] ) ) { return new \WP_Error( 'empty_term_talent_name', __( 'A podcast artist or author name is required.' ) ); } - // Validate Empty Podcast Summary. - if ( empty( trim( $args['podcasting_summary'] ) ) ) { + // Require podcast description. + $args['podcasting_summary'] = trim( $args['podcasting_summary'] ); + if ( empty( $args['podcasting_summary'] ) ) { return new \WP_Error( 'empty_term_summary', __( 'A podcast summary is required.' ) ); } - // Validate Podcast Image. - if ( empty( trim( $args['podcasting_image'] ) ) ) { + // Require podcast image. + $args['podcasting_image'] = trim( $args['podcasting_image'] ); + if ( empty( $args['podcasting_image'] ) ) { return new \WP_Error( 'empty_term_cover_image', __( 'A podcast cover image is required.' ) ); } + // Require podcast category. + $args['podcating_category_1'] = trim( $args['podcasting_category_1'] ); + $args['podcating_category_2'] = trim( $args['podcasting_category_2'] ); + $args['podcating_category_3'] = trim( $args['podcasting_category_3'] ); + + $is_missing_category = $is_onboarding_step_1 ? + empty( $args['podcasting_category_1'] ) : + ( + empty( $args['podcasting_category_1'] ) && + empty( $args['podcasting_category_2'] ) && + empty( $args['podcasting_category_3'] ) + ); + + if ( $is_missing_category ) { + return new \WP_Error( 'empty_term_category', __( 'A podcast category is required.' ) ); + } + return $term; } From 0061620ac1de9696ee6114eef7d70c6a92fe905f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20Ivani=C4=87?= Date: Wed, 8 Mar 2023 13:33:59 +0100 Subject: [PATCH 06/22] Fix typo --- includes/admin/onboarding.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/admin/onboarding.php b/includes/admin/onboarding.php index 2887750a..8293bdd7 100644 --- a/includes/admin/onboarding.php +++ b/includes/admin/onboarding.php @@ -65,7 +65,7 @@ function onboarding_action_handler() { } $podcast_name = isset( $_POST['podcast-name'] ) ? sanitize_text_field( wp_unslash( $_POST['podcast-name'] ) ) : null; - $podcasting_talent_name = isset( $_POST['podcast-name'] ) ? sanitize_text_field( wp_unslash( $_POST['podcast-artist'] ) ) : null; + $podcasting_talent_name = isset( $_POST['podcast-artist'] ) ? sanitize_text_field( wp_unslash( $_POST['podcast-artist'] ) ) : null; $podcast_description = isset( $_POST['podcast-description'] ) ? sanitize_text_field( wp_unslash( $_POST['podcast-description'] ) ) : null; $podcast_category = isset( $_POST['podcast-category'] ) ? sanitize_text_field( wp_unslash( $_POST['podcast-category'] ) ) : null; $podcast_cover_id = isset( $_POST['podcast-cover-image-id'] ) ? absint( wp_unslash( $_POST['podcast-cover-image-id'] ) ) : null; From 26dd7f7abee9795ce4fbe7d88ba1ec53810ff5a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20Ivani=C4=87?= Date: Wed, 8 Mar 2023 16:45:24 +0100 Subject: [PATCH 07/22] Clean up --- includes/datatypes.php | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/includes/datatypes.php b/includes/datatypes.php index ace35d44..0f4b7876 100644 --- a/includes/datatypes.php +++ b/includes/datatypes.php @@ -867,8 +867,7 @@ function validate_taxonomy_fields( $term, $taxonomy, $args ) { $referer = sanitize_text_field( $_POST['_wp_http_referer'] ); $is_onboarding_step_1 = false !== strpos( $referer, 'page=simple-podcasting-onboarding&step=1' ); - $term = trim( $term ); - if ( ! $is_onboarding_step_1 && empty( $term ) ) { + if ( ! $is_onboarding_step_1 && empty( trim( $term ) ) ) { return new \WP_Error( 'empty_term_name', __( 'A podcast name is required.' ) ); } @@ -878,40 +877,32 @@ function validate_taxonomy_fields( $term, $taxonomy, $args ) { } // Require podcast name. - $args['tag-name'] = trim( $args['tag-name'] ); - if ( empty( $args['tag-name'] ) ) { + if ( empty( trim( $args['tag-name'] ) ) ) { return new \WP_Error( 'empty_term_name', __( 'A podcast name is required.' ) ); } // Require podcast author name only on term edit screen. - $args['podcasting_talent_name'] = trim( $args['podcasting_talent_name'] ); - if ( empty( $args['podcasting_talent_name'] ) ) { + if ( empty( trim( $args['podcasting_talent_name'] ) ) ) { return new \WP_Error( 'empty_term_talent_name', __( 'A podcast artist or author name is required.' ) ); } // Require podcast description. - $args['podcasting_summary'] = trim( $args['podcasting_summary'] ); - if ( empty( $args['podcasting_summary'] ) ) { + if ( empty( trim( $args['podcasting_summary'] ) ) ) { return new \WP_Error( 'empty_term_summary', __( 'A podcast summary is required.' ) ); } // Require podcast image. - $args['podcasting_image'] = trim( $args['podcasting_image'] ); - if ( empty( $args['podcasting_image'] ) ) { + if ( empty( trim( $args['podcasting_image'] ) ) ) { return new \WP_Error( 'empty_term_cover_image', __( 'A podcast cover image is required.' ) ); } // Require podcast category. - $args['podcating_category_1'] = trim( $args['podcasting_category_1'] ); - $args['podcating_category_2'] = trim( $args['podcasting_category_2'] ); - $args['podcating_category_3'] = trim( $args['podcasting_category_3'] ); - $is_missing_category = $is_onboarding_step_1 ? - empty( $args['podcasting_category_1'] ) : + empty( trim( $args['podcasting_category_1'] ) ) : ( - empty( $args['podcasting_category_1'] ) && - empty( $args['podcasting_category_2'] ) && - empty( $args['podcasting_category_3'] ) + empty( trim( $args['podcasting_category_1'] ) ) && + empty( trim( $args['podcasting_category_2'] ) ) && + empty( trim( $args['podcasting_category_3'] ) ) ); if ( $is_missing_category ) { From 1d8388d4ac4c5700b37f017d29a6041696b13f2d Mon Sep 17 00:00:00 2001 From: Garth Gutenberg Date: Thu, 30 Mar 2023 07:49:17 -0500 Subject: [PATCH 08/22] Show Category 1 as required --- includes/datatypes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/datatypes.php b/includes/datatypes.php index 0f4b7876..a7c6fef6 100644 --- a/includes/datatypes.php +++ b/includes/datatypes.php @@ -582,7 +582,7 @@ function get_meta_fields() { ), array( 'slug' => 'podcasting_category_1', - 'title' => __( 'Category 1', 'simple-podcasting' ), + 'title' => __( 'Category 1 (required)', 'simple-podcasting' ), 'type' => 'select', 'options' => get_podcasting_categories_options(), ), From ffd10247611d00bc408e2e12555e1c906598c30c Mon Sep 17 00:00:00 2001 From: Garth Gutenberg Date: Thu, 30 Mar 2023 09:01:50 -0500 Subject: [PATCH 09/22] Fix tests --- package-lock.json | 20 ++++++++++++ package.json | 1 + tests/cypress/integration/block.test.js | 34 ++++++++++++++++---- tests/cypress/integration/onboarding.test.js | 8 +++-- tests/cypress/integration/taxonomy.test.js | 26 +++++++-------- tests/cypress/support/functions.js | 32 ++++++++++++++++++ 6 files changed, 98 insertions(+), 23 deletions(-) diff --git a/package-lock.json b/package-lock.json index 91b66bd4..dfb4071f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "copy-webpack-plugin": "^11.0.0", "cypress": "^11.2.0", "cypress-file-upload": "^5.0.8", + "cypress-localstorage-commands": "^2.2.2", "eslint-plugin-cypress": "^2.12.1", "husky": "^8.0.1", "json-schema": ">=0.4.0", @@ -6793,6 +6794,18 @@ "cypress": ">3.0.0" } }, + "node_modules/cypress-localstorage-commands": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/cypress-localstorage-commands/-/cypress-localstorage-commands-2.2.2.tgz", + "integrity": "sha512-mONoaxoMAFOSsF+SIDx30hysD3XOY8kPLxjtORdZWDj3sJs+u03XOddTDETPIbLch0DWy5Dtyw1ou3EsFGYZAQ==", + "dev": true, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "cypress": ">=2.1.0" + } + }, "node_modules/cypress/node_modules/@types/node": { "version": "14.18.34", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.34.tgz", @@ -23552,6 +23565,13 @@ "dev": true, "requires": {} }, + "cypress-localstorage-commands": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/cypress-localstorage-commands/-/cypress-localstorage-commands-2.2.2.tgz", + "integrity": "sha512-mONoaxoMAFOSsF+SIDx30hysD3XOY8kPLxjtORdZWDj3sJs+u03XOddTDETPIbLch0DWy5Dtyw1ou3EsFGYZAQ==", + "dev": true, + "requires": {} + }, "damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", diff --git a/package.json b/package.json index 2acd9fde..debe3b1e 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "copy-webpack-plugin": "^11.0.0", "cypress": "^11.2.0", "cypress-file-upload": "^5.0.8", + "cypress-localstorage-commands": "^2.2.2", "eslint-plugin-cypress": "^2.12.1", "husky": "^8.0.1", "json-schema": ">=0.4.0", diff --git a/tests/cypress/integration/block.test.js b/tests/cypress/integration/block.test.js index f6986856..a23f096e 100644 --- a/tests/cypress/integration/block.test.js +++ b/tests/cypress/integration/block.test.js @@ -1,18 +1,36 @@ +import 'cypress-localstorage-commands'; +const { populatePodcast } = require('../support/functions'); + describe('Admin can publish posts with podcast block', () => { const taxonomy = 'Remote work'; + before(() => { + const userId = '1'; + cy.setLocalStorage( + `WP_DATA_USER_${userId}`, + JSON.stringify({ + 'core/edit-post': { + preferences: { + features: { + welcomeGuide: false, + }, + }, + }, + }) + ); + }); + if (Cypress.env('HAS_BLOCK_EDITOR')) { it('Can insert the block and publish the post', () => { cy.login(); cy.uploadMedia('tests/cypress/fixtures/example.jpg'); cy.createTerm(taxonomy, 'podcasting_podcasts', { beforeSave: () => { - cy.get('#podcasting_talent_name').type('Person Doe'); - cy.get('#podcasting_summary').type('Lorem ipsum dolor'); - cy.get('#image-podcasting_image').click(); - cy.get('#menu-item-browse').click(); - cy.get('.attachments-wrapper').click(); - cy.get('.media-button-select').click(); + populatePodcast({ + author: 'Person Doe', + summary: 'Lorem ipsum dolor', + category: 'arts:food', + }); }, }); @@ -29,7 +47,9 @@ describe('Admin can publish posts with podcast block', () => { .first() .as('block-search'); cy.get('@block-search').click().type('Podcast'); - cy.get('.editor-block-list-item-podcasting-podcast').click(); + cy.get('.editor-block-list-item-podcasting-podcast', { + timeout: 4000, + }).click(); cy.get('.edit-post-header-toolbar__inserter-toggle').click(); cy.get( '.wp-block-podcasting-podcast input[type="file"]' diff --git a/tests/cypress/integration/onboarding.test.js b/tests/cypress/integration/onboarding.test.js index 91a5662a..df37bdb3 100644 --- a/tests/cypress/integration/onboarding.test.js +++ b/tests/cypress/integration/onboarding.test.js @@ -30,7 +30,11 @@ describe('Onboarding tests', () => { .closest('form') .submit(); - cy.contains('Taxonomy error: A podcast name is required..'); + cy.get('input[name="podcast-name"]').then(($input) => { + expect($input[0].validationMessage).to.eq( + 'Please fill out this field.' + ); + }); }); it('Should pass onboarding', () => { @@ -45,7 +49,7 @@ describe('Onboarding tests', () => { ); cy.get('#simple-podcasting__upload-cover-image').click(); cy.get('#menu-item-browse').click(); - cy.get('.attachments-wrapper').click(); + cy.get('.attachments-wrapper li:first-of-type').click(); cy.get('.media-button-select').click(); cy.get('select[name=podcast-category]').select('Arts'); cy.get('#simple-podcasting__create-podcast-button') diff --git a/tests/cypress/integration/taxonomy.test.js b/tests/cypress/integration/taxonomy.test.js index 4b537b7c..866a176d 100644 --- a/tests/cypress/integration/taxonomy.test.js +++ b/tests/cypress/integration/taxonomy.test.js @@ -1,4 +1,4 @@ -const { randomName } = require('../support/functions'); +const { randomName, populatePodcast } = require('../support/functions'); describe('Admin can create and update podcast taxonomy', () => { before(() => { @@ -33,12 +33,11 @@ describe('Admin can create and update podcast taxonomy', () => { cy.uploadMedia('tests/cypress/fixtures/example.jpg'); cy.createTerm('Remote work', 'podcasting_podcasts', { beforeSave: () => { - cy.get('#podcasting_talent_name').type('Person Doe'); - cy.get('#podcasting_summary').type('Lorem ipsum dolor'); - cy.get('#image-podcasting_image').click(); - cy.get('#menu-item-browse').click(); - cy.get('.attachments-wrapper').click(); - cy.get('.media-button-select').click(); + populatePodcast({ + author: 'Person Doe', + summary: 'Lorem ipsum dolor', + category: 'arts:food', + }); }, }); cy.get('.row-title').should('have.text', 'Remote work'); @@ -89,13 +88,12 @@ describe('Admin can create and update podcast taxonomy', () => { cy.uploadMedia('tests/cypress/fixtures/example.jpg'); cy.createTerm(podcastName, 'podcasting_podcasts', { beforeSave: () => { - cy.get('#podcasting_type_of_show').select(typeOfShowName); - cy.get('#podcasting_talent_name').type('Person Doe'); - cy.get('#podcasting_summary').type('Lorem ipsum dolor'); - cy.get('#image-podcasting_image').click(); - cy.get('#menu-item-browse').click(); - cy.get('.attachments-wrapper').click(); - cy.get('.media-button-select').click(); + populatePodcast({ + typeOfShowName, + author: 'Person Doe', + summary: 'Lorem ipsum dolor', + category: 'arts:food', + }); }, }).then((term) => { cy.visit( diff --git a/tests/cypress/support/functions.js b/tests/cypress/support/functions.js index c638c025..fce4a0e8 100644 --- a/tests/cypress/support/functions.js +++ b/tests/cypress/support/functions.js @@ -1 +1,33 @@ export const randomName = () => Math.random().toString(16).substring(7); + +/** + * Populates a podcast taxonomy. + * @param {Object} args The podcast data to populate. + * @param {string} args.typeOfShowName The type of show. + * @param {string} args.author The author of the podcast. + * @param {string} args.summary The summary of the podcast. + * @param {string} args.category The category of the podcast. + */ +export const populatePodcast = (args) => { + for (const [key, value] of Object.entries(args)) { + switch (key) { + case 'typeOfShowName': + cy.get('#podcasting_type_of_show').select(value); + break; + case 'author': + cy.get('#podcasting_talent_name').type(value); + break; + case 'summary': + cy.get('#podcasting_summary').type(value); + break; + case 'category': + cy.get('#podcasting_category_1').select(value); + break; + } + } + // Get first image from media library. + cy.get('#image-podcasting_image').click(); + cy.get('#menu-item-browse').click(); + cy.get('.attachments-wrapper li:first-of-type').click(); + cy.get('.media-button-select').click(); +}; From efe31f0009478cedb28e4b504ad4778eac7f7917 Mon Sep 17 00:00:00 2001 From: Garth Gutenberg Date: Thu, 30 Mar 2023 11:47:20 -0500 Subject: [PATCH 10/22] Fix tests on WP < 6.1 --- includes/datatypes.php | 7 +++- tests/cypress/integration/onboarding.test.js | 17 ++++------ tests/cypress/support/functions.js | 35 ++++++++++++++++---- 3 files changed, 41 insertions(+), 18 deletions(-) diff --git a/includes/datatypes.php b/includes/datatypes.php index a7c6fef6..f9ca289b 100644 --- a/includes/datatypes.php +++ b/includes/datatypes.php @@ -858,7 +858,7 @@ function get_podcasting_language_options() { * * @return string */ -function validate_taxonomy_fields( $term, $taxonomy, $args ) { +function validate_taxonomy_fields( $term, $taxonomy, $args = [] ) { // Bailout, if not the podcasts taxonomy. if ( 'podcasting_podcasts' !== $taxonomy ) { return $term; @@ -871,6 +871,11 @@ function validate_taxonomy_fields( $term, $taxonomy, $args ) { return new \WP_Error( 'empty_term_name', __( 'A podcast name is required.' ) ); } + // The third argument was only introduced in the `pre_insert_term` filter in WP 6.1, so bail if it's empty. + if ( empty( $args ) ) { + return $term; + } + if ( $is_onboarding_step_1 ) { $args['tag-name'] = sanitize_title( $_POST['podcast-name'] ); $args['podcasting_category_1'] = sanitize_text_field( $_POST['podcast-category'] ); diff --git a/tests/cypress/integration/onboarding.test.js b/tests/cypress/integration/onboarding.test.js index df37bdb3..0161f727 100644 --- a/tests/cypress/integration/onboarding.test.js +++ b/tests/cypress/integration/onboarding.test.js @@ -1,4 +1,4 @@ -const { randomName } = require('../support/functions'); +const { randomName, populatePodcast } = require('../support/functions'); describe('Onboarding tests', () => { before(() => { @@ -43,15 +43,12 @@ describe('Onboarding tests', () => { const podcastName = 'Onboarding ' + randomName(); cy.get('input[name=podcast-name]').click().type(podcastName); - cy.get('input[name="podcast-artist"]').type('Person Doe'); - cy.get('textarea[name="podcast-description"]').type( - 'Lorem ipsum dolor' - ); - cy.get('#simple-podcasting__upload-cover-image').click(); - cy.get('#menu-item-browse').click(); - cy.get('.attachments-wrapper li:first-of-type').click(); - cy.get('.media-button-select').click(); - cy.get('select[name=podcast-category]').select('Arts'); + populatePodcast({ + author: 'Person Doe', + summary: 'Lorem ipsum dolor', + category: 'Arts', + onboarding: true, + }); cy.get('#simple-podcasting__create-podcast-button') .closest('form') .submit(); diff --git a/tests/cypress/support/functions.js b/tests/cypress/support/functions.js index fce4a0e8..c6cbc483 100644 --- a/tests/cypress/support/functions.js +++ b/tests/cypress/support/functions.js @@ -1,5 +1,11 @@ export const randomName = () => Math.random().toString(16).substring(7); +export const getFirstImage = () => { + cy.get('#menu-item-browse').click(); + cy.get('ul.attachments li:first-of-type').click(); + cy.get('.media-button-select').click(); +}; + /** * Populates a podcast taxonomy. * @param {Object} args The podcast data to populate. @@ -7,6 +13,7 @@ export const randomName = () => Math.random().toString(16).substring(7); * @param {string} args.author The author of the podcast. * @param {string} args.summary The summary of the podcast. * @param {string} args.category The category of the podcast. + * @param {boolean} args.onboarding Whether this is onboarding or not. */ export const populatePodcast = (args) => { for (const [key, value] of Object.entries(args)) { @@ -15,19 +22,33 @@ export const populatePodcast = (args) => { cy.get('#podcasting_type_of_show').select(value); break; case 'author': - cy.get('#podcasting_talent_name').type(value); + if (args.onboarding) { + cy.get('input[name="podcast-artist"]').type(value); + } else { + cy.get('#podcasting_talent_name').type(value); + } break; case 'summary': - cy.get('#podcasting_summary').type(value); + if (args.onboarding) { + cy.get('textarea[name="podcast-description"]').type(value); + } else { + cy.get('#podcasting_summary').type(value); + } break; case 'category': - cy.get('#podcasting_category_1').select(value); + if (args.onboarding) { + cy.get('select[name=podcast-category]').select(value); + } else { + cy.get('#podcasting_category_1').select(value); + } break; } } // Get first image from media library. - cy.get('#image-podcasting_image').click(); - cy.get('#menu-item-browse').click(); - cy.get('.attachments-wrapper li:first-of-type').click(); - cy.get('.media-button-select').click(); + if (args.onboarding) { + cy.get('#simple-podcasting__upload-cover-image').click(); + } else { + cy.get('#image-podcasting_image').click(); + } + getFirstImage(); }; From eedb30a340aeb0247e8c422492f993c1e993322e Mon Sep 17 00:00:00 2001 From: Siddharth Thevaril Date: Fri, 14 Apr 2023 15:55:09 +0530 Subject: [PATCH 11/22] Update includes/admin/onboarding.php Co-authored-by: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> --- includes/admin/onboarding.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/admin/onboarding.php b/includes/admin/onboarding.php index 8293bdd7..5d991058 100644 --- a/includes/admin/onboarding.php +++ b/includes/admin/onboarding.php @@ -95,7 +95,7 @@ function() use ( $result ) { return; } - /** Add podcast author. */ + /* Add podcast author. */ if ( $podcasting_talent_name ) { update_term_meta( $result['term_id'], 'podcasting_talent_name', $podcasting_talent_name ); } From 2e8d8a864e0ad3069938225b046695c73bffd1bb Mon Sep 17 00:00:00 2001 From: Siddharth Thevaril Date: Fri, 14 Apr 2023 15:56:12 +0530 Subject: [PATCH 12/22] Update includes/datatypes.php Co-authored-by: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> --- includes/datatypes.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/datatypes.php b/includes/datatypes.php index f9ca289b..970799c8 100644 --- a/includes/datatypes.php +++ b/includes/datatypes.php @@ -877,8 +877,8 @@ function validate_taxonomy_fields( $term, $taxonomy, $args = [] ) { } if ( $is_onboarding_step_1 ) { - $args['tag-name'] = sanitize_title( $_POST['podcast-name'] ); - $args['podcasting_category_1'] = sanitize_text_field( $_POST['podcast-category'] ); + $args['tag-name'] = sanitize_title( wp_unslash( $_POST['podcast-name'] ) ); + $args['podcasting_category_1'] = sanitize_text_field( wp_unslash( $_POST['podcast-category'] ) ); } // Require podcast name. From e0114b7db57fb15845e40de343ed80e8e10076d4 Mon Sep 17 00:00:00 2001 From: Siddharth Thevaril Date: Fri, 14 Apr 2023 15:56:27 +0530 Subject: [PATCH 13/22] Update includes/datatypes.php Co-authored-by: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> --- includes/datatypes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/datatypes.php b/includes/datatypes.php index 970799c8..ecf7f7a3 100644 --- a/includes/datatypes.php +++ b/includes/datatypes.php @@ -868,7 +868,7 @@ function validate_taxonomy_fields( $term, $taxonomy, $args = [] ) { $is_onboarding_step_1 = false !== strpos( $referer, 'page=simple-podcasting-onboarding&step=1' ); if ( ! $is_onboarding_step_1 && empty( trim( $term ) ) ) { - return new \WP_Error( 'empty_term_name', __( 'A podcast name is required.' ) ); + return new \WP_Error( 'empty_term_name', __( 'A podcast name is required.', 'simple-podcasting' ) ); } // The third argument was only introduced in the `pre_insert_term` filter in WP 6.1, so bail if it's empty. From 74db92ee594dd250ee5ac0c87c67c748b2d7e52f Mon Sep 17 00:00:00 2001 From: faisal-alvi Date: Wed, 10 May 2023 18:30:57 +0530 Subject: [PATCH 14/22] using `parse_str` --- includes/datatypes.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/includes/datatypes.php b/includes/datatypes.php index ecf7f7a3..cbd79ad1 100644 --- a/includes/datatypes.php +++ b/includes/datatypes.php @@ -864,8 +864,12 @@ function validate_taxonomy_fields( $term, $taxonomy, $args = [] ) { return $term; } - $referer = sanitize_text_field( $_POST['_wp_http_referer'] ); - $is_onboarding_step_1 = false !== strpos( $referer, 'page=simple-podcasting-onboarding&step=1' ); + $referer = sanitize_text_field( $_POST['_wp_http_referer'] ); + $query_string = parse_url( $referer, PHP_URL_QUERY ); + parse_str( $query_string, $query ); + + $is_onboarding_step_1 = isset( $query['page'] ) && isset( $query['step'] ) + && 'simple-podcasting-onboarding' === $query['page'] && '1' === $query['step']; if ( ! $is_onboarding_step_1 && empty( trim( $term ) ) ) { return new \WP_Error( 'empty_term_name', __( 'A podcast name is required.', 'simple-podcasting' ) ); From 558766c998d0742634c5bcb967389e2a814c5c47 Mon Sep 17 00:00:00 2001 From: faisal-alvi Date: Wed, 10 May 2023 18:33:42 +0530 Subject: [PATCH 15/22] adding text domain --- includes/datatypes.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/includes/datatypes.php b/includes/datatypes.php index cbd79ad1..b4519caf 100644 --- a/includes/datatypes.php +++ b/includes/datatypes.php @@ -887,22 +887,22 @@ function validate_taxonomy_fields( $term, $taxonomy, $args = [] ) { // Require podcast name. if ( empty( trim( $args['tag-name'] ) ) ) { - return new \WP_Error( 'empty_term_name', __( 'A podcast name is required.' ) ); + return new \WP_Error( 'empty_term_name', __( 'A podcast name is required.', 'simple-podcasting' ) ); } // Require podcast author name only on term edit screen. if ( empty( trim( $args['podcasting_talent_name'] ) ) ) { - return new \WP_Error( 'empty_term_talent_name', __( 'A podcast artist or author name is required.' ) ); + return new \WP_Error( 'empty_term_talent_name', __( 'A podcast artist or author name is required.', 'simple-podcasting' ) ); } // Require podcast description. if ( empty( trim( $args['podcasting_summary'] ) ) ) { - return new \WP_Error( 'empty_term_summary', __( 'A podcast summary is required.' ) ); + return new \WP_Error( 'empty_term_summary', __( 'A podcast summary is required.', 'simple-podcasting' ) ); } // Require podcast image. if ( empty( trim( $args['podcasting_image'] ) ) ) { - return new \WP_Error( 'empty_term_cover_image', __( 'A podcast cover image is required.' ) ); + return new \WP_Error( 'empty_term_cover_image', __( 'A podcast cover image is required.', 'simple-podcasting' ) ); } // Require podcast category. @@ -915,7 +915,7 @@ function validate_taxonomy_fields( $term, $taxonomy, $args = [] ) { ); if ( $is_missing_category ) { - return new \WP_Error( 'empty_term_category', __( 'A podcast category is required.' ) ); + return new \WP_Error( 'empty_term_category', __( 'A podcast category is required.', 'simple-podcasting' ) ); } return $term; From d3acfb549386ce1612a0251e125b5ad16dce21c2 Mon Sep 17 00:00:00 2001 From: Faisal Alvi Date: Wed, 10 May 2023 18:36:46 +0530 Subject: [PATCH 16/22] adding `aria-describedby` for the descriptive text Co-authored-by: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> --- includes/admin/views/onboarding-page-one.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/admin/views/onboarding-page-one.php b/includes/admin/views/onboarding-page-one.php index 6081eff0..1a75ca0a 100644 --- a/includes/admin/views/onboarding-page-one.php +++ b/includes/admin/views/onboarding-page-one.php @@ -35,8 +35,8 @@
- - + +
From 6105d35b834e8f452ae7f823f9c17220bad4f5ab Mon Sep 17 00:00:00 2001 From: Faisal Alvi Date: Wed, 10 May 2023 18:37:08 +0530 Subject: [PATCH 17/22] adding `aria-describedby` for the descriptive text Co-authored-by: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> --- includes/admin/views/onboarding-page-one.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/admin/views/onboarding-page-one.php b/includes/admin/views/onboarding-page-one.php index 1a75ca0a..0444b9aa 100644 --- a/includes/admin/views/onboarding-page-one.php +++ b/includes/admin/views/onboarding-page-one.php @@ -28,8 +28,8 @@
- -
+ +
From 127ddb114441e91ac2cc28e066dfa91b2199b1f5 Mon Sep 17 00:00:00 2001 From: Faisal Alvi Date: Wed, 10 May 2023 18:37:20 +0530 Subject: [PATCH 18/22] adding `aria-describedby` for the descriptive text Co-authored-by: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> --- includes/admin/views/onboarding-page-one.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/admin/views/onboarding-page-one.php b/includes/admin/views/onboarding-page-one.php index 0444b9aa..de1779d0 100644 --- a/includes/admin/views/onboarding-page-one.php +++ b/includes/admin/views/onboarding-page-one.php @@ -21,8 +21,8 @@
- -
+ +
From f03f62a40875108b97d53be0b98d092ed6abe41c Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Mon, 22 May 2023 13:49:48 +1000 Subject: [PATCH 19/22] Relate lable to podcast name field. --- includes/admin/views/onboarding-page-one.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/admin/views/onboarding-page-one.php b/includes/admin/views/onboarding-page-one.php index de1779d0..4053567e 100644 --- a/includes/admin/views/onboarding-page-one.php +++ b/includes/admin/views/onboarding-page-one.php @@ -14,8 +14,8 @@
- -
+ +
From b7efe5dd5e15abb71577fdd394ec32741699c513 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Mon, 22 May 2023 13:52:54 +1000 Subject: [PATCH 20/22] Add aria-describedby attribute to input fields. --- includes/admin/views/onboarding-page-one.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/includes/admin/views/onboarding-page-one.php b/includes/admin/views/onboarding-page-one.php index 4053567e..8c9d65d2 100644 --- a/includes/admin/views/onboarding-page-one.php +++ b/includes/admin/views/onboarding-page-one.php @@ -15,42 +15,42 @@
-
-
+
+
-
-
+
+
-
-
+
+
- +
-
+
- $option_label ) : ?> -
+
From b422d8b2a87e454b46b062f3da77edcbd7826f29 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Mon, 22 May 2023 14:00:04 +1000 Subject: [PATCH 21/22] Add missing quote mark to ID attribute. --- includes/admin/views/onboarding-page-one.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/admin/views/onboarding-page-one.php b/includes/admin/views/onboarding-page-one.php index 8c9d65d2..fbbeeeb6 100644 --- a/includes/admin/views/onboarding-page-one.php +++ b/includes/admin/views/onboarding-page-one.php @@ -15,7 +15,7 @@
-
+
From 074fa18dd06e9024b0d3336a59c48f0f45fb259c Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Mon, 22 May 2023 14:00:38 +1000 Subject: [PATCH 22/22] Relate label to podcast category dropdown. --- includes/admin/views/onboarding-page-one.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/admin/views/onboarding-page-one.php b/includes/admin/views/onboarding-page-one.php index fbbeeeb6..ebca8e1e 100644 --- a/includes/admin/views/onboarding-page-one.php +++ b/includes/admin/views/onboarding-page-one.php @@ -44,8 +44,8 @@
- - $option_label ) : ?>