diff --git a/includes/admin/onboarding.php b/includes/admin/onboarding.php index f66d1881..5d991058 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..ebca8e1e 100644 --- a/includes/admin/views/onboarding-page-one.php +++ b/includes/admin/views/onboarding-page-one.php @@ -14,36 +14,43 @@
- -
-
+ +
+
+
+ + +
+ +
+
- -
-
+ +
+
- - + +
-
+
- - $option_label ) : ?> -
+
diff --git a/includes/datatypes.php b/includes/datatypes.php index 96af2059..42fff3fc 100644 --- a/includes/datatypes.php +++ b/includes/datatypes.php @@ -502,7 +502,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( @@ -512,7 +512,7 @@ function get_meta_fields() { ), array( 'slug' => 'podcasting_summary', - 'title' => __( 'Summary', 'simple-podcasting' ), + 'title' => __( 'Summary (required)', 'simple-podcasting' ), 'type' => 'textarea', ), array( @@ -538,7 +538,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' ), ), @@ -560,7 +560,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(), ), @@ -826,3 +826,77 @@ function get_podcasting_language_options() { ) ); } + +/** + * Validate Podcast Taxonomy Fields. + * + * @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 $term; + } + + $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' ) ); + } + + // 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( wp_unslash( $_POST['podcast-name'] ) ); + $args['podcasting_category_1'] = sanitize_text_field( wp_unslash( $_POST['podcast-category'] ) ); + } + + // Require podcast name. + if ( empty( trim( $args['tag-name'] ) ) ) { + 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.', 'simple-podcasting' ) ); + } + + // Require podcast description. + if ( empty( trim( $args['podcasting_summary'] ) ) ) { + 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.', 'simple-podcasting' ) ); + } + + // Require podcast category. + $is_missing_category = $is_onboarding_step_1 ? + empty( trim( $args['podcasting_category_1'] ) ) : + ( + empty( trim( $args['podcasting_category_1'] ) ) && + empty( trim( $args['podcasting_category_2'] ) ) && + empty( trim( $args['podcasting_category_3'] ) ) + ); + + if ( $is_missing_category ) { + return new \WP_Error( 'empty_term_category', __( 'A podcast category is required.', 'simple-podcasting' ) ); + } + + return $term; +} + +add_filter( 'pre_insert_term', __NAMESPACE__ . '\validate_taxonomy_fields', 10, 3 ); diff --git a/package-lock.json b/package-lock.json index 8f99d7ae..1029400c 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", "cypress-mochawesome-reporter": "^3.4.0", "eslint-plugin-cypress": "^2.12.1", "husky": "^8.0.1", @@ -6801,6 +6802,18 @@ "cypress": ">3.0.0" } }, + "node_modules/cypress-localstorage-commands": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/cypress-localstorage-commands/-/cypress-localstorage-commands-2.2.3.tgz", + "integrity": "sha512-EUEaHzbstw9AsEheIqr+RyXuxIzUS64nBBwl+Q4/mSdzfXpfcaV1nrHF+6H9zbTuFVTc+oWu6eC1l8aSjiWW6w==", + "dev": true, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "cypress": ">=2.1.0" + } + }, "node_modules/cypress-mochawesome-reporter": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/cypress-mochawesome-reporter/-/cypress-mochawesome-reporter-3.4.0.tgz", @@ -24175,6 +24188,13 @@ "dev": true, "requires": {} }, + "cypress-localstorage-commands": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/cypress-localstorage-commands/-/cypress-localstorage-commands-2.2.3.tgz", + "integrity": "sha512-EUEaHzbstw9AsEheIqr+RyXuxIzUS64nBBwl+Q4/mSdzfXpfcaV1nrHF+6H9zbTuFVTc+oWu6eC1l8aSjiWW6w==", + "dev": true, + "requires": {} + }, "cypress-mochawesome-reporter": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/cypress-mochawesome-reporter/-/cypress-mochawesome-reporter-3.4.0.tgz", diff --git a/package.json b/package.json index 2fc30f65..0b98ede5 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", "cypress-mochawesome-reporter": "^3.4.0", "eslint-plugin-cypress": "^2.12.1", "husky": "^8.0.1", diff --git a/tests/cypress/fixtures/example.jpg b/tests/cypress/fixtures/example.jpg new file mode 100644 index 00000000..b0f2052e Binary files /dev/null and b/tests/cypress/fixtures/example.jpg differ diff --git a/tests/cypress/integration/block.test.js b/tests/cypress/integration/block.test.js index 6976fdf9..a23f096e 100644 --- a/tests/cypress/integration/block.test.js +++ b/tests/cypress/integration/block.test.js @@ -1,11 +1,38 @@ +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.createTerm(taxonomy, 'podcasting_podcasts'); + cy.uploadMedia('tests/cypress/fixtures/example.jpg'); + cy.createTerm(taxonomy, 'podcasting_podcasts', { + beforeSave: () => { + populatePodcast({ + author: 'Person Doe', + summary: 'Lorem ipsum dolor', + category: 'arts:food', + }); + }, + }); cy.visit('/wp-admin/post-new.php'); cy.closeWelcomeGuide(); @@ -20,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 e0f335cf..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(() => { @@ -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,11 @@ describe('Onboarding tests', () => { .closest('form') .submit(); - cy.contains('Show name is required.'); - cy.contains('Podcast category 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', () => { @@ -39,8 +43,12 @@ describe('Onboarding tests', () => { const podcastName = 'Onboarding ' + randomName(); cy.get('input[name=podcast-name]').click().type(podcastName); - 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/integration/taxonomy.test.js b/tests/cypress/integration/taxonomy.test.js index 7ebc1cec..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(() => { @@ -30,7 +30,16 @@ 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: () => { + populatePodcast({ + author: 'Person Doe', + summary: 'Lorem ipsum dolor', + category: 'arts:food', + }); + }, + }); cy.get('.row-title').should('have.text', 'Remote work'); }); @@ -76,9 +85,15 @@ 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); + 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..c6cbc483 100644 --- a/tests/cypress/support/functions.js +++ b/tests/cypress/support/functions.js @@ -1 +1,54 @@ 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. + * @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. + * @param {boolean} args.onboarding Whether this is onboarding or not. + */ +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': + if (args.onboarding) { + cy.get('input[name="podcast-artist"]').type(value); + } else { + cy.get('#podcasting_talent_name').type(value); + } + break; + case 'summary': + if (args.onboarding) { + cy.get('textarea[name="podcast-description"]').type(value); + } else { + cy.get('#podcasting_summary').type(value); + } + break; + case 'category': + 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. + if (args.onboarding) { + cy.get('#simple-podcasting__upload-cover-image').click(); + } else { + cy.get('#image-podcasting_image').click(); + } + getFirstImage(); +};