Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion assets/js/checkout.js
Original file line number Diff line number Diff line change
Expand Up @@ -795,7 +795,6 @@
this.password_strength_checker = new window.WU_PasswordStrength({
pass1: pass1_el,
result: jQuery('#pass-strength-result'),
minStrength: 3,
onValidityChange(isValid) {

that.valid_password = isValid;
Expand Down Expand Up @@ -1078,6 +1077,11 @@
// Setup inline login handlers if prompt is visible
this.setup_inline_login_handlers();

// Re-initialize password strength if field appeared after mount
if (! this.password_strength_checker && jQuery('#field-password').length) {
this.init_password_strength();
}

});

},
Expand Down
32 changes: 32 additions & 0 deletions tests/e2e/cypress/fixtures/set-password-strength.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php
/**
* Set the minimum password strength setting for e2e testing.
*
* Usage: wp eval-file set-password-strength.php -- <strength>
* Where <strength> is one of: medium, strong, super_strong
*
* Outputs the new setting value as confirmation.
*/

$args = $GLOBALS['argv'] ?? [];

// The strength value is passed as a positional argument after '--'.
$strength = end($args);

$valid = ['medium', 'strong', 'super_strong'];

if (! in_array($strength, $valid, true)) {
echo wp_json_encode([

Check failure on line 19 in tests/e2e/cypress/fixtures/set-password-strength.php

View workflow job for this annotation

GitHub Actions / Code Quality Checks

Opening parenthesis of a multi-line function call must be the last content on the line
'error' => 'Invalid strength value',
'value' => $strength,
'allowed' => $valid,
]);

Check failure on line 23 in tests/e2e/cypress/fixtures/set-password-strength.php

View workflow job for this annotation

GitHub Actions / Code Quality Checks

Closing parenthesis of a multi-line function call must be on a line by itself
exit(1);
}

wu_save_setting('minimum_password_strength', $strength);

echo wp_json_encode([

Check failure on line 29 in tests/e2e/cypress/fixtures/set-password-strength.php

View workflow job for this annotation

GitHub Actions / Code Quality Checks

Opening parenthesis of a multi-line function call must be the last content on the line
'success' => true,

Check warning on line 30 in tests/e2e/cypress/fixtures/set-password-strength.php

View workflow job for this annotation

GitHub Actions / Code Quality Checks

Array double arrow not aligned correctly; expected 1 space(s) between "'success'" and double arrow, but found 2.
'setting' => wu_get_setting('minimum_password_strength'),

Check warning on line 31 in tests/e2e/cypress/fixtures/set-password-strength.php

View workflow job for this annotation

GitHub Actions / Code Quality Checks

Array double arrow not aligned correctly; expected 1 space(s) between "'setting'" and double arrow, but found 2.
]);

Check failure on line 32 in tests/e2e/cypress/fixtures/set-password-strength.php

View workflow job for this annotation

GitHub Actions / Code Quality Checks

Closing parenthesis of a multi-line function call must be on a line by itself
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
describe("Password Strength Enforcement", () => {
/**
* Helper: set the password strength setting via WP-CLI fixture.
*/
function setPasswordStrength(level) {
const containerPath =
"/var/www/html/wp-content/plugins/ultimate-multisite/tests/e2e/cypress/fixtures/set-password-strength.php";
cy.exec(
`npx wp-env run tests-cli wp eval-file ${containerPath} -- ${level}`,
{ timeout: 60000 }
).then((result) => {
const data = JSON.parse(result.stdout.trim());
expect(data.setting).to.equal(level);
});
}

/**
* Helper: visit the register page with a fresh browser state.
*/
function visitRegisterPage() {
cy.clearCookies();
cy.visit("/register", { failOnStatusCode: false });

// Wait for checkout form to render
cy.get("#field-password", { timeout: 30000 }).should("be.visible");
cy.wait(2000);
}

/**
* Helper: type a password and return validation state from Vue.
*/
function typePasswordAndGetState(password) {
cy.get("#field-password").clear().type(password);
cy.wait(500); // Allow strength checker to process

return cy.window().then((win) => {
const vue = win.document.querySelector("#wu_form").__vue__;
const score = win.wp.passwordStrength.meter(password, [], "");
const label = win.document.querySelector(
"#pass-strength-result"
).textContent;

return {
valid_password: vue.valid_password,
zxcvbn_score: score,
strength_label: label,
minStrength: vue.password_strength_checker
? vue.password_strength_checker.options.minStrength
: null,
};
});
}

// ─────────────────────────────────────────────────
// Test: Strong setting (default)
// ─────────────────────────────────────────────────
describe("Strong setting (zxcvbn score >= 4)", () => {
before(() => {
setPasswordStrength("strong");
});

beforeEach(() => {
visitRegisterPage();
});

it("should reject a medium-strength password (score 3)", () => {
typePasswordAndGetState("Summer2025!xyz").then((state) => {
expect(state.zxcvbn_score).to.equal(3);
expect(state.valid_password).to.equal(false);
expect(state.minStrength).to.equal(4);
expect(state.strength_label).to.equal("Medium");
});
});

it("should accept a strong password (score 4)", () => {
typePasswordAndGetState("correct horse battery").then((state) => {
expect(state.zxcvbn_score).to.equal(4);
expect(state.valid_password).to.equal(true);
expect(state.strength_label).to.equal("Strong");
});
});

it("should reject a weak password (score 2)", () => {
typePasswordAndGetState("Butterfly923!").then((state) => {
expect(state.zxcvbn_score).to.be.lessThan(3);
expect(state.valid_password).to.equal(false);
});
});
});

// ─────────────────────────────────────────────────
// Test: Super Strong setting (score >= 4 + char rules)
// ─────────────────────────────────────────────────
describe("Super Strong setting (score >= 4 + character rules)", () => {
before(() => {
setPasswordStrength("super_strong");
});

beforeEach(() => {
visitRegisterPage();
});

it("should reject a score-3 password even with all character types", () => {
typePasswordAndGetState("Summer2025!xyz").then((state) => {
expect(state.zxcvbn_score).to.equal(3);
expect(state.valid_password).to.equal(false);
// Should show Medium, not Super Strong
expect(state.strength_label).to.not.contain("Super Strong");
});
});

it("should reject a score-4 password missing character types", () => {
// score 4 but no uppercase, numbers, or special chars
typePasswordAndGetState("correct horse battery").then((state) => {
expect(state.zxcvbn_score).to.equal(4);
expect(state.valid_password).to.equal(false);
expect(state.strength_label).to.contain("Required:");
});
});

it("should reject a password shorter than 12 characters", () => {
// Strong score but too short for super_strong
typePasswordAndGetState("xK9#mL2$vN").then((state) => {
expect(state.valid_password).to.equal(false);
});
});

it("should accept a password with score 4 and all character types", () => {
typePasswordAndGetState("xK9#mL2$vN5@qR").then((state) => {
expect(state.zxcvbn_score).to.equal(4);
expect(state.valid_password).to.equal(true);
expect(state.strength_label).to.equal("Super Strong");
});
});
});

// ─────────────────────────────────────────────────
// Test: Medium setting (score >= 3)
// ─────────────────────────────────────────────────
describe("Medium setting (zxcvbn score >= 3)", () => {
before(() => {
setPasswordStrength("medium");
});

beforeEach(() => {
visitRegisterPage();
});

it("should accept a medium-strength password (score 3)", () => {
typePasswordAndGetState("Summer2025!xyz").then((state) => {
expect(state.zxcvbn_score).to.equal(3);
expect(state.valid_password).to.equal(true);
expect(state.minStrength).to.equal(3);
});
});

it("should reject a weak password (score 2)", () => {
typePasswordAndGetState("Butterfly923!").then((state) => {
expect(state.zxcvbn_score).to.be.lessThan(3);
expect(state.valid_password).to.equal(false);
});
});
});

// ─────────────────────────────────────────────────
// Cleanup: restore to strong (default)
// ─────────────────────────────────────────────────
after(() => {
setPasswordStrength("strong");
});
});
Loading