feat(discount-codes): enhance discount code form with accessibility attributes and data-testid
This commit is contained in:
parent
d216a886d0
commit
014b3d3f48
Binary file not shown.
|
|
@ -0,0 +1,472 @@
|
|||
describe("Discount Codes Advanced Features", () => {
|
||||
beforeEach(() => {
|
||||
cy.login();
|
||||
cy.visit("/discount-codes");
|
||||
cy.waitForLoading();
|
||||
});
|
||||
|
||||
describe("Form Validation", () => {
|
||||
beforeEach(() => {
|
||||
cy.get('[title="کد تخفیف جدید"]').click();
|
||||
});
|
||||
|
||||
it("should validate code format and uniqueness", () => {
|
||||
// Test invalid characters (if implemented)
|
||||
cy.get('input[name="code"]').type("TEST CODE"); // Space in code
|
||||
cy.get('input[name="name"]').type("Test Name");
|
||||
cy.get('select[name="type"]').select("percentage");
|
||||
cy.get('input[name="value"]').type("10");
|
||||
cy.get('select[name="status"]').select("active");
|
||||
cy.get('select[name="application_level"]').select("invoice");
|
||||
|
||||
// Try to submit - may show validation error for invalid characters
|
||||
cy.get('button[type="submit"]').click();
|
||||
|
||||
// Clear and use valid code
|
||||
cy.get('input[name="code"]').clear().type("TESTCODE123");
|
||||
cy.get('button[type="submit"]').click();
|
||||
|
||||
cy.url().should("include", "/discount-codes");
|
||||
});
|
||||
|
||||
it("should validate name length constraints", () => {
|
||||
cy.get('input[name="code"]').type("NAMETEST");
|
||||
|
||||
// Test name too long
|
||||
cy.get('input[name="name"]').type("A".repeat(101));
|
||||
cy.contains("نام نباید بیشتر از ۱۰۰ کاراکتر باشد").should("be.visible");
|
||||
|
||||
// Clear and use valid name
|
||||
cy.get('input[name="name"]').clear().type("Valid Name");
|
||||
cy.get('select[name="type"]').select("percentage");
|
||||
cy.get('input[name="value"]').type("10");
|
||||
cy.get('select[name="status"]').select("active");
|
||||
cy.get('select[name="application_level"]').select("invoice");
|
||||
});
|
||||
|
||||
it("should validate description length", () => {
|
||||
cy.get('input[name="code"]').type("DESCTEST");
|
||||
cy.get('input[name="name"]').type("Description Test");
|
||||
|
||||
// Test description too long
|
||||
cy.get('textarea[name="description"]').type("A".repeat(501));
|
||||
cy.contains("توضیحات نباید بیشتر از ۵۰۰ کاراکتر باشد").should(
|
||||
"be.visible"
|
||||
);
|
||||
});
|
||||
|
||||
it("should validate percentage values", () => {
|
||||
cy.get('input[name="code"]').type("PERCENTTEST");
|
||||
cy.get('input[name="name"]').type("Percent Test");
|
||||
cy.get('select[name="type"]').select("percentage");
|
||||
|
||||
// Test negative value
|
||||
cy.get('input[name="value"]').type("-10");
|
||||
cy.contains("مقدار باید بیشتر از صفر باشد").should("be.visible");
|
||||
|
||||
// Test zero value
|
||||
cy.get('input[name="value"]').clear().type("0");
|
||||
cy.contains("مقدار باید بیشتر از صفر باشد").should("be.visible");
|
||||
|
||||
// Test valid value
|
||||
cy.get('input[name="value"]').clear().type("25");
|
||||
});
|
||||
|
||||
it("should validate usage limits", () => {
|
||||
cy.get('input[name="code"]').type("USAGETEST");
|
||||
cy.get('input[name="name"]').type("Usage Test");
|
||||
cy.get('select[name="type"]').select("percentage");
|
||||
cy.get('input[name="value"]').type("10");
|
||||
cy.get('select[name="status"]').select("active");
|
||||
cy.get('select[name="application_level"]').select("invoice");
|
||||
|
||||
// Test invalid usage limit
|
||||
cy.get('input[name="usage_limit"]').type("0");
|
||||
cy.contains("حداقل ۱ بار استفاده").should("be.visible");
|
||||
|
||||
// Test invalid user usage limit
|
||||
cy.get('input[name="user_usage_limit"]').type("0");
|
||||
cy.contains("حداقل ۱ بار استفاده").should("be.visible");
|
||||
});
|
||||
|
||||
it("should validate amount constraints", () => {
|
||||
cy.get('input[name="code"]').type("AMOUNTTEST");
|
||||
cy.get('input[name="name"]').type("Amount Test");
|
||||
cy.get('select[name="type"]').select("fixed");
|
||||
cy.get('input[name="value"]').type("1000");
|
||||
cy.get('select[name="status"]').select("active");
|
||||
cy.get('select[name="application_level"]').select("invoice");
|
||||
|
||||
// Test invalid minimum purchase amount
|
||||
cy.get('input[name="min_purchase_amount"]').type("0");
|
||||
cy.contains("مبلغ باید بیشتر از صفر باشد").should("be.visible");
|
||||
|
||||
// Test invalid maximum discount amount
|
||||
cy.get('input[name="max_discount_amount"]').type("-100");
|
||||
cy.contains("مبلغ باید بیشتر از صفر باشد").should("be.visible");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Date and Time Handling", () => {
|
||||
beforeEach(() => {
|
||||
cy.get('[title="کد تخفیف جدید"]').click();
|
||||
// Fill required fields
|
||||
cy.get('input[name="code"]').type("DATETEST");
|
||||
cy.get('input[name="name"]').type("Date Test");
|
||||
cy.get('select[name="type"]').select("percentage");
|
||||
cy.get('input[name="value"]').type("10");
|
||||
cy.get('select[name="status"]').select("active");
|
||||
cy.get('select[name="application_level"]').select("invoice");
|
||||
});
|
||||
|
||||
it("should handle date range validation", () => {
|
||||
// Set end date before start date
|
||||
cy.get('input[name="valid_from"]').type("2024-12-31T23:59");
|
||||
cy.get('input[name="valid_to"]').type("2024-01-01T00:00");
|
||||
|
||||
// Form should still accept it (backend validation)
|
||||
cy.get('button[type="submit"]').click();
|
||||
cy.url().should("include", "/discount-codes");
|
||||
});
|
||||
|
||||
it("should preserve datetime values in edit mode", () => {
|
||||
// Set specific datetime values
|
||||
const fromDate = "2024-06-01T10:30";
|
||||
const toDate = "2024-06-30T18:45";
|
||||
|
||||
cy.get('input[name="valid_from"]').type(fromDate);
|
||||
cy.get('input[name="valid_to"]').type(toDate);
|
||||
|
||||
cy.get('button[type="submit"]').click();
|
||||
cy.url().should("include", "/discount-codes");
|
||||
|
||||
// Edit the created discount code
|
||||
cy.contains("DATETEST")
|
||||
.parent()
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.get('[title="ویرایش"]').click();
|
||||
});
|
||||
|
||||
// Values should be preserved
|
||||
cy.get('input[name="valid_from"]').should("have.value", fromDate);
|
||||
cy.get('input[name="valid_to"]').should("have.value", toDate);
|
||||
});
|
||||
});
|
||||
|
||||
describe("User Restrictions", () => {
|
||||
beforeEach(() => {
|
||||
cy.get('[title="کد تخفیف جدید"]').click();
|
||||
// Fill required fields
|
||||
cy.get('input[name="code"]').type("USERTEST");
|
||||
cy.get('input[name="name"]').type("User Test");
|
||||
cy.get('select[name="type"]').select("percentage");
|
||||
cy.get('input[name="value"]').type("15");
|
||||
cy.get('select[name="status"]').select("active");
|
||||
cy.get('select[name="application_level"]').select("invoice");
|
||||
});
|
||||
|
||||
it("should handle user group selection", () => {
|
||||
// Test all user group options
|
||||
cy.get('select[name="user_restrictions.user_group"]').select("new");
|
||||
cy.get('select[name="user_restrictions.user_group"]').should(
|
||||
"have.value",
|
||||
"new"
|
||||
);
|
||||
|
||||
cy.get('select[name="user_restrictions.user_group"]').select("loyal");
|
||||
cy.get('select[name="user_restrictions.user_group"]').should(
|
||||
"have.value",
|
||||
"loyal"
|
||||
);
|
||||
|
||||
cy.get('select[name="user_restrictions.user_group"]').select("all");
|
||||
cy.get('select[name="user_restrictions.user_group"]').should(
|
||||
"have.value",
|
||||
"all"
|
||||
);
|
||||
});
|
||||
|
||||
it("should handle purchase count restrictions", () => {
|
||||
cy.get('input[name="user_restrictions.min_purchase_count"]').type("2");
|
||||
cy.get('input[name="user_restrictions.max_purchase_count"]').type("10");
|
||||
cy.get('input[name="user_restrictions.referrer_user_id"]').type("456");
|
||||
|
||||
cy.get('button[type="submit"]').click();
|
||||
cy.url().should("include", "/discount-codes");
|
||||
cy.contains("کد تخفیف با موفقیت ایجاد شد").should("be.visible");
|
||||
});
|
||||
|
||||
it("should warn about conflicting user restrictions", () => {
|
||||
// Check both new users only and loyal users only
|
||||
cy.get('input[name="user_restrictions.new_users_only"]').check();
|
||||
cy.get('input[name="user_restrictions.loyal_users_only"]').check();
|
||||
|
||||
// Warning should be visible
|
||||
cy.contains(
|
||||
"new_users_only و loyal_users_only نمیتوانند همزمان فعال باشند"
|
||||
).should("be.visible");
|
||||
|
||||
// Uncheck one
|
||||
cy.get('input[name="user_restrictions.new_users_only"]').uncheck();
|
||||
|
||||
cy.get('button[type="submit"]').click();
|
||||
cy.url().should("include", "/discount-codes");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Application Levels", () => {
|
||||
beforeEach(() => {
|
||||
cy.get('[title="کد تخفیف جدید"]').click();
|
||||
cy.get('input[name="code"]').type("APPTEST");
|
||||
cy.get('input[name="name"]').type("Application Test");
|
||||
cy.get('input[name="value"]').type("100");
|
||||
cy.get('select[name="status"]').select("active");
|
||||
});
|
||||
|
||||
it("should handle product fee application with fee percentage type", () => {
|
||||
cy.get('select[name="type"]').select("fee_percentage");
|
||||
cy.get('select[name="application_level"]').select("product_fee");
|
||||
|
||||
cy.get('button[type="submit"]').click();
|
||||
cy.url().should("include", "/discount-codes");
|
||||
cy.contains("کد تخفیف با موفقیت ایجاد شد").should("be.visible");
|
||||
});
|
||||
|
||||
it("should test all application level combinations", () => {
|
||||
const types = ["percentage", "fixed", "fee_percentage"];
|
||||
const applications = [
|
||||
"invoice",
|
||||
"category",
|
||||
"product",
|
||||
"shipping",
|
||||
"product_fee",
|
||||
];
|
||||
|
||||
types.forEach((type, typeIndex) => {
|
||||
applications.forEach((app, appIndex) => {
|
||||
if (typeIndex > 0 || appIndex > 0) {
|
||||
// Generate unique code for each combination
|
||||
cy.get('input[name="code"]')
|
||||
.clear()
|
||||
.type(`TEST${typeIndex}${appIndex}`);
|
||||
}
|
||||
|
||||
cy.get('select[name="type"]').select(type);
|
||||
cy.get('select[name="application_level"]').select(app);
|
||||
|
||||
// For fee_percentage, use smaller values
|
||||
if (type === "fee_percentage") {
|
||||
cy.get('input[name="value"]').clear().type("5");
|
||||
} else if (type === "percentage") {
|
||||
cy.get('input[name="value"]').clear().type("10");
|
||||
} else {
|
||||
cy.get('input[name="value"]').clear().type("1000");
|
||||
}
|
||||
|
||||
cy.get('button[type="submit"]').click();
|
||||
cy.url().should("include", "/discount-codes");
|
||||
|
||||
// Go back to create page for next iteration (except last)
|
||||
if (
|
||||
!(
|
||||
typeIndex === types.length - 1 &&
|
||||
appIndex === applications.length - 1
|
||||
)
|
||||
) {
|
||||
cy.get('[title="کد تخفیف جدید"]').click();
|
||||
cy.get('input[name="name"]').type("Application Test");
|
||||
cy.get('select[name="status"]').select("active");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Meta Information", () => {
|
||||
it("should handle meta fields properly", () => {
|
||||
cy.get('[title="کد تخفیف جدید"]').click();
|
||||
|
||||
cy.get('input[name="code"]').type("METATEST");
|
||||
cy.get('input[name="name"]').type("Meta Test");
|
||||
cy.get('select[name="type"]').select("percentage");
|
||||
cy.get('input[name="value"]').type("20");
|
||||
cy.get('select[name="status"]').select("active");
|
||||
cy.get('select[name="application_level"]').select("invoice");
|
||||
|
||||
// Set meta fields
|
||||
cy.get('input[name="meta.campaign"]').type("winter_sale_2024");
|
||||
cy.get('input[name="meta.category"]').type("seasonal_promotion");
|
||||
|
||||
cy.get('button[type="submit"]').click();
|
||||
cy.url().should("include", "/discount-codes");
|
||||
cy.contains("کد تخفیف با موفقیت ایجاد شد").should("be.visible");
|
||||
|
||||
// Verify meta fields are preserved in edit
|
||||
cy.contains("METATEST")
|
||||
.parent()
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.get('[title="ویرایش"]').click();
|
||||
});
|
||||
|
||||
cy.get('input[name="meta.campaign"]').should(
|
||||
"have.value",
|
||||
"winter_sale_2024"
|
||||
);
|
||||
cy.get('input[name="meta.category"]').should(
|
||||
"have.value",
|
||||
"seasonal_promotion"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("List Page Features", () => {
|
||||
it("should display correct value format based on type", () => {
|
||||
// Create different types of discounts to test display
|
||||
const testCodes = [
|
||||
{ code: "DISPLAYPERCENT", type: "percentage", value: "25" },
|
||||
{ code: "DISPLAYFIXED", type: "fixed", value: "50000" },
|
||||
{ code: "DISPLAYFEE", type: "fee_percentage", value: "5" },
|
||||
];
|
||||
|
||||
testCodes.forEach((testCode) => {
|
||||
cy.get('[title="کد تخفیف جدید"]').click();
|
||||
cy.get('input[name="code"]').type(testCode.code);
|
||||
cy.get('input[name="name"]').type(`Display Test ${testCode.type}`);
|
||||
cy.get('select[name="type"]').select(testCode.type);
|
||||
cy.get('input[name="value"]').type(testCode.value);
|
||||
cy.get('select[name="status"]').select("active");
|
||||
cy.get('select[name="application_level"]').select(
|
||||
testCode.type === "fee_percentage" ? "product_fee" : "invoice"
|
||||
);
|
||||
cy.get('button[type="submit"]').click();
|
||||
cy.url().should("include", "/discount-codes");
|
||||
});
|
||||
|
||||
// Check display formats
|
||||
cy.contains("DISPLAYPERCENT")
|
||||
.parent()
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.contains("25%").should("be.visible");
|
||||
});
|
||||
|
||||
cy.contains("DISPLAYFIXED")
|
||||
.parent()
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.contains("50000 تومان").should("be.visible");
|
||||
});
|
||||
|
||||
cy.contains("DISPLAYFEE")
|
||||
.parent()
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.contains("5%").should("be.visible");
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle pagination properly", () => {
|
||||
// This test assumes there are enough items to paginate
|
||||
// Check if pagination exists
|
||||
cy.get('nav[aria-label="Pagination Navigation"]').should("exist");
|
||||
|
||||
// Test pagination controls if they exist
|
||||
cy.get('nav[aria-label="Pagination Navigation"]').within(() => {
|
||||
cy.get("button").should("have.length.greaterThan", 0);
|
||||
});
|
||||
});
|
||||
|
||||
it("should sort columns when sortable", () => {
|
||||
// Click on sortable column headers
|
||||
cy.get("th").contains("کد").click();
|
||||
cy.wait(500);
|
||||
|
||||
cy.get("th").contains("نام").click();
|
||||
cy.wait(500);
|
||||
|
||||
// Verify table content changes (basic check)
|
||||
cy.get("table tbody tr").should("have.length.greaterThan", 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Error Handling", () => {
|
||||
it("should handle network errors gracefully", () => {
|
||||
// Intercept network requests and simulate errors
|
||||
cy.intercept("POST", "**/discount/", { statusCode: 500 }).as(
|
||||
"createError"
|
||||
);
|
||||
|
||||
cy.get('[title="کد تخفیف جدید"]').click();
|
||||
cy.get('input[name="code"]').type("ERRORTEST");
|
||||
cy.get('input[name="name"]').type("Error Test");
|
||||
cy.get('select[name="type"]').select("percentage");
|
||||
cy.get('input[name="value"]').type("10");
|
||||
cy.get('select[name="status"]').select("active");
|
||||
cy.get('select[name="application_level"]').select("invoice");
|
||||
|
||||
cy.get('button[type="submit"]').click();
|
||||
|
||||
cy.wait("@createError");
|
||||
cy.contains("خطا در ایجاد کد تخفیف").should("be.visible");
|
||||
});
|
||||
|
||||
it("should handle loading states", () => {
|
||||
cy.get('[title="کد تخفیف جدید"]').click();
|
||||
|
||||
// Intercept with delay to see loading state
|
||||
cy.intercept("POST", "**/discount/", { delay: 2000, statusCode: 200 }).as(
|
||||
"createSlow"
|
||||
);
|
||||
|
||||
cy.get('input[name="code"]').type("LOADTEST");
|
||||
cy.get('input[name="name"]').type("Load Test");
|
||||
cy.get('select[name="type"]').select("percentage");
|
||||
cy.get('input[name="value"]').type("10");
|
||||
cy.get('select[name="status"]').select("active");
|
||||
cy.get('select[name="application_level"]').select("invoice");
|
||||
|
||||
cy.get('button[type="submit"]').click();
|
||||
|
||||
// Check loading state
|
||||
cy.get('button[type="submit"]').should("be.disabled");
|
||||
cy.get(".animate-spin").should("be.visible");
|
||||
|
||||
cy.wait("@createSlow");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Responsive Design", () => {
|
||||
it("should work on mobile viewport", () => {
|
||||
cy.viewport("iphone-6");
|
||||
|
||||
cy.get('[title="کد تخفیف جدید"]').should("be.visible");
|
||||
cy.get('[title="کد تخفیف جدید"]').click();
|
||||
|
||||
cy.contains("ایجاد کد تخفیف").should("be.visible");
|
||||
|
||||
// Form should be usable on mobile
|
||||
cy.get('input[name="code"]').type("MOBILETEST");
|
||||
cy.get('input[name="name"]').type("Mobile Test");
|
||||
cy.get('select[name="type"]').select("percentage");
|
||||
cy.get('input[name="value"]').type("10");
|
||||
cy.get('select[name="status"]').select("active");
|
||||
cy.get('select[name="application_level"]').select("invoice");
|
||||
|
||||
cy.get('button[type="submit"]').should("be.visible").click();
|
||||
cy.url().should("include", "/discount-codes");
|
||||
});
|
||||
|
||||
it("should work on tablet viewport", () => {
|
||||
cy.viewport("ipad-2");
|
||||
|
||||
cy.get('[title="کد تخفیف جدید"]').should("be.visible");
|
||||
cy.get("table").should("be.visible");
|
||||
|
||||
// Test form on tablet
|
||||
cy.get('[title="کد تخفیف جدید"]').click();
|
||||
cy.get(".grid").should("be.visible"); // Grid layout should work
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,373 @@
|
|||
import { discountTemplates, apiMocks } from "../support/discount-codes-helpers";
|
||||
|
||||
describe("Discount Codes - Complete E2E Tests", () => {
|
||||
beforeEach(() => {
|
||||
cy.login();
|
||||
});
|
||||
|
||||
describe("Navigation and Basic UI", () => {
|
||||
it("should display discount codes list page correctly", () => {
|
||||
cy.visit("/discount-codes");
|
||||
cy.waitForLoading();
|
||||
|
||||
cy.contains("مدیریت کدهای تخفیف").should("be.visible");
|
||||
cy.get('[title="کد تخفیف جدید"]').should("be.visible");
|
||||
});
|
||||
|
||||
it("should navigate to create page", () => {
|
||||
cy.navigateToCreateDiscount();
|
||||
cy.contains("ایجاد کد تخفیف").should("be.visible");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Form Validation", () => {
|
||||
beforeEach(() => {
|
||||
cy.navigateToCreateDiscount();
|
||||
});
|
||||
|
||||
it("should validate required fields", () => {
|
||||
cy.getByTestId("submit-discount-button").should("be.disabled");
|
||||
|
||||
cy.fillBasicDiscountInfo({
|
||||
code: "TEST123",
|
||||
name: "Test Discount",
|
||||
});
|
||||
cy.getByTestId("submit-discount-button").should("be.disabled");
|
||||
|
||||
cy.fillDiscountSettings({
|
||||
type: "percentage",
|
||||
value: "10",
|
||||
status: "active",
|
||||
applicationLevel: "invoice",
|
||||
});
|
||||
|
||||
cy.getByTestId("submit-discount-button").should("not.be.disabled");
|
||||
});
|
||||
|
||||
it("should validate field lengths and formats", () => {
|
||||
// Test code length
|
||||
cy.getByTestId("discount-code-input").type("AB");
|
||||
cy.getByTestId("discount-name-input").type("Test");
|
||||
cy.get(".text-red-600").should("contain", "کد باید حداقل ۳ کاراکتر باشد");
|
||||
|
||||
// Test code too long
|
||||
cy.getByTestId("discount-code-input").clear().type("A".repeat(51));
|
||||
cy.get(".text-red-600").should(
|
||||
"contain",
|
||||
"کد نباید بیشتر از ۵۰ کاراکتر باشد"
|
||||
);
|
||||
|
||||
// Test name too long
|
||||
cy.getByTestId("discount-name-input").clear().type("A".repeat(101));
|
||||
cy.get(".text-red-600").should(
|
||||
"contain",
|
||||
"نام نباید بیشتر از ۱۰۰ کاراکتر باشد"
|
||||
);
|
||||
|
||||
// Test description too long
|
||||
cy.getByTestId("discount-description-textarea").type("A".repeat(501));
|
||||
cy.get(".text-red-600").should(
|
||||
"contain",
|
||||
"توضیحات نباید بیشتر از ۵۰۰ کاراکتر باشد"
|
||||
);
|
||||
});
|
||||
|
||||
it("should validate numeric fields", () => {
|
||||
cy.fillBasicDiscountInfo({
|
||||
code: "NUMTEST",
|
||||
name: "Number Test",
|
||||
});
|
||||
|
||||
// Test negative value
|
||||
cy.getByTestId("discount-value-input").type("-10");
|
||||
cy.get(".text-red-600").should("contain", "مقدار باید بیشتر از صفر باشد");
|
||||
|
||||
// Test zero value
|
||||
cy.getByTestId("discount-value-input").clear().type("0");
|
||||
cy.get(".text-red-600").should("contain", "مقدار باید بیشتر از صفر باشد");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Discount Creation", () => {
|
||||
beforeEach(() => {
|
||||
// Mock successful API responses
|
||||
cy.intercept("GET", "**/discount/**", apiMocks.discountsList).as(
|
||||
"getDiscounts"
|
||||
);
|
||||
cy.intercept("POST", "**/discount/**", (req) => {
|
||||
return apiMocks.successfulCreation(req.body);
|
||||
}).as("createDiscount");
|
||||
});
|
||||
|
||||
it("should create basic percentage discount", () => {
|
||||
cy.createDiscountCode(discountTemplates.basicPercentage);
|
||||
cy.wait("@createDiscount");
|
||||
cy.contains("کد تخفیف با موفقیت ایجاد شد").should("be.visible");
|
||||
});
|
||||
|
||||
it("should create fixed amount discount", () => {
|
||||
cy.createDiscountCode(discountTemplates.fixedAmount);
|
||||
cy.wait("@createDiscount");
|
||||
cy.contains("کد تخفیف با موفقیت ایجاد شد").should("be.visible");
|
||||
});
|
||||
|
||||
it("should create fee percentage discount", () => {
|
||||
cy.createDiscountCode(discountTemplates.feePercentage);
|
||||
cy.wait("@createDiscount");
|
||||
cy.contains("کد تخفیف با موفقیت ایجاد شد").should("be.visible");
|
||||
});
|
||||
|
||||
it("should create discount with user restrictions", () => {
|
||||
cy.createDiscountCode(discountTemplates.loyalUsers);
|
||||
cy.wait("@createDiscount");
|
||||
cy.contains("کد تخفیف با موفقیت ایجاد شد").should("be.visible");
|
||||
});
|
||||
|
||||
it("should create time-based discount with all features", () => {
|
||||
cy.createDiscountCode(discountTemplates.timeBasedDiscount);
|
||||
cy.wait("@createDiscount");
|
||||
cy.contains("کد تخفیف با موفقیت ایجاد شد").should("be.visible");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Error Handling", () => {
|
||||
it("should handle validation errors from API", () => {
|
||||
cy.intercept("POST", "**/discount/**", apiMocks.validationError).as(
|
||||
"validationError"
|
||||
);
|
||||
|
||||
cy.navigateToCreateDiscount();
|
||||
cy.fillBasicDiscountInfo(discountTemplates.basicPercentage);
|
||||
cy.fillDiscountSettings(discountTemplates.basicPercentage);
|
||||
cy.submitDiscountForm();
|
||||
|
||||
cy.wait("@validationError");
|
||||
cy.contains("کد تخفیف تکراری است").should("be.visible");
|
||||
});
|
||||
|
||||
it("should handle server errors", () => {
|
||||
cy.intercept("POST", "**/discount/**", apiMocks.serverError).as(
|
||||
"serverError"
|
||||
);
|
||||
|
||||
cy.navigateToCreateDiscount();
|
||||
cy.fillBasicDiscountInfo(discountTemplates.basicPercentage);
|
||||
cy.fillDiscountSettings(discountTemplates.basicPercentage);
|
||||
cy.submitDiscountForm();
|
||||
|
||||
cy.wait("@serverError");
|
||||
cy.contains("خطا در ایجاد کد تخفیف").should("be.visible");
|
||||
});
|
||||
|
||||
it("should handle loading states", () => {
|
||||
cy.intercept("POST", "**/discount/**", {
|
||||
delay: 2000,
|
||||
...apiMocks.successfulCreation(discountTemplates.basicPercentage),
|
||||
}).as("slowCreate");
|
||||
|
||||
cy.navigateToCreateDiscount();
|
||||
cy.fillBasicDiscountInfo(discountTemplates.basicPercentage);
|
||||
cy.fillDiscountSettings(discountTemplates.basicPercentage);
|
||||
cy.submitDiscountForm();
|
||||
|
||||
// Check loading state
|
||||
cy.getByTestId("submit-discount-button").should("be.disabled");
|
||||
cy.get(".animate-spin").should("be.visible");
|
||||
|
||||
cy.wait("@slowCreate");
|
||||
});
|
||||
});
|
||||
|
||||
describe("List Page Features", () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept("GET", "**/discount/**", apiMocks.discountsList).as(
|
||||
"getDiscounts"
|
||||
);
|
||||
cy.visit("/discount-codes");
|
||||
cy.wait("@getDiscounts");
|
||||
});
|
||||
|
||||
it("should search discount codes", () => {
|
||||
cy.searchDiscountCode("SAVE20");
|
||||
cy.contains("SAVE20").should("be.visible");
|
||||
|
||||
cy.searchDiscountCode("NONEXISTENT");
|
||||
cy.contains("هیچ کد تخفیفی یافت نشد").should("be.visible");
|
||||
});
|
||||
|
||||
it("should clear filters", () => {
|
||||
cy.searchDiscountCode("TEST");
|
||||
cy.clearDiscountFilters();
|
||||
cy.get('input[placeholder*="جستجو"]').should("have.value", "");
|
||||
});
|
||||
|
||||
it("should display discount codes with correct formatting", () => {
|
||||
cy.contains("SAVE20").should("be.visible");
|
||||
cy.contains("20%").should("be.visible");
|
||||
cy.get(".bg-green-100").should("contain", "فعال");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Edit Functionality", () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept("GET", "**/discount/**", apiMocks.discountsList).as(
|
||||
"getDiscounts"
|
||||
);
|
||||
cy.intercept("GET", "**/discount/1", {
|
||||
statusCode: 200,
|
||||
body: {
|
||||
id: 1,
|
||||
code: "SAVE20",
|
||||
name: "20% Off Discount",
|
||||
description: "Get 20% off on your purchase",
|
||||
type: "percentage",
|
||||
value: 20,
|
||||
status: "active",
|
||||
application_level: "invoice",
|
||||
},
|
||||
}).as("getDiscount");
|
||||
cy.intercept("PUT", "**/discount/1", {
|
||||
statusCode: 200,
|
||||
body: { message: "updated successfully" },
|
||||
}).as("updateDiscount");
|
||||
});
|
||||
|
||||
it("should edit existing discount code", () => {
|
||||
cy.visit("/discount-codes");
|
||||
cy.wait("@getDiscounts");
|
||||
|
||||
cy.contains("SAVE20")
|
||||
.parent()
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.get('[title="ویرایش"]').click();
|
||||
});
|
||||
|
||||
cy.wait("@getDiscount");
|
||||
cy.url().should("include", "/edit");
|
||||
|
||||
cy.getByTestId("discount-name-input")
|
||||
.clear()
|
||||
.type("Updated Discount Name");
|
||||
cy.submitDiscountForm();
|
||||
|
||||
cy.wait("@updateDiscount");
|
||||
cy.contains("کد تخفیف با موفقیت بهروزرسانی شد").should("be.visible");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Delete Functionality", () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept("GET", "**/discount/**", apiMocks.discountsList).as(
|
||||
"getDiscounts"
|
||||
);
|
||||
cy.intercept("DELETE", "**/discount/**", {
|
||||
statusCode: 200,
|
||||
body: { message: "deleted successfully" },
|
||||
}).as("deleteDiscount");
|
||||
});
|
||||
|
||||
it("should delete discount code", () => {
|
||||
cy.visit("/discount-codes");
|
||||
cy.wait("@getDiscounts");
|
||||
|
||||
cy.contains("SAVE20")
|
||||
.parent()
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.get('[title="حذف"]').click();
|
||||
});
|
||||
|
||||
cy.contains("آیا از حذف این کد تخفیف اطمینان دارید؟").should(
|
||||
"be.visible"
|
||||
);
|
||||
cy.contains("button", "حذف").click();
|
||||
|
||||
cy.wait("@deleteDiscount");
|
||||
cy.contains("کد تخفیف با موفقیت حذف شد").should("be.visible");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Responsive Design", () => {
|
||||
it("should work on mobile devices", () => {
|
||||
cy.viewport("iphone-6");
|
||||
cy.navigateToCreateDiscount();
|
||||
|
||||
cy.fillBasicDiscountInfo({
|
||||
code: "MOBILE123",
|
||||
name: "Mobile Test",
|
||||
});
|
||||
|
||||
cy.fillDiscountSettings({
|
||||
type: "percentage",
|
||||
value: "10",
|
||||
status: "active",
|
||||
applicationLevel: "invoice",
|
||||
});
|
||||
|
||||
cy.getByTestId("submit-discount-button").should("be.visible");
|
||||
});
|
||||
|
||||
it("should work on tablets", () => {
|
||||
cy.viewport("ipad-2");
|
||||
cy.visit("/discount-codes");
|
||||
cy.waitForLoading();
|
||||
|
||||
cy.get("table").should("be.visible");
|
||||
cy.get('[title="کد تخفیف جدید"]').should("be.visible");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Accessibility", () => {
|
||||
it("should be keyboard navigable", () => {
|
||||
cy.navigateToCreateDiscount();
|
||||
|
||||
cy.getByTestId("discount-code-input").focus();
|
||||
cy.focused().should("have.attr", "data-testid", "discount-code-input");
|
||||
|
||||
cy.focused().tab();
|
||||
cy.focused().should("have.attr", "data-testid", "discount-name-input");
|
||||
});
|
||||
|
||||
it("should have proper ARIA labels", () => {
|
||||
cy.navigateToCreateDiscount();
|
||||
|
||||
cy.get("label").should("have.length.greaterThan", 5);
|
||||
cy.get("input[required]").should("have.length.greaterThan", 3);
|
||||
});
|
||||
|
||||
it("should announce errors to screen readers", () => {
|
||||
cy.navigateToCreateDiscount();
|
||||
|
||||
cy.getByTestId("discount-code-input").type("AB");
|
||||
cy.get(".text-red-600").should("have.attr", "role", "alert");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Performance", () => {
|
||||
it("should load create page quickly", () => {
|
||||
const startTime = Date.now();
|
||||
cy.navigateToCreateDiscount();
|
||||
cy.getByTestId("discount-code-input")
|
||||
.should("be.visible")
|
||||
.then(() => {
|
||||
const loadTime = Date.now() - startTime;
|
||||
expect(loadTime).to.be.lessThan(3000); // Should load within 3 seconds
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle large forms efficiently", () => {
|
||||
cy.navigateToCreateDiscount();
|
||||
|
||||
// Fill form quickly without delays
|
||||
cy.getByTestId("discount-code-input").type("PERF123");
|
||||
cy.getByTestId("discount-name-input").type("Performance Test");
|
||||
cy.getByTestId("discount-description-textarea").type("A".repeat(400));
|
||||
cy.getByTestId("discount-type-select").select("percentage");
|
||||
cy.getByTestId("discount-value-input").type("25");
|
||||
|
||||
// Form should remain responsive
|
||||
cy.getByTestId("submit-discount-button").should("not.be.disabled");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,251 @@
|
|||
/// <reference types="../support" />
|
||||
|
||||
describe("Discount Codes Management - Fixed", () => {
|
||||
beforeEach(() => {
|
||||
cy.login();
|
||||
cy.visit("/discount-codes");
|
||||
cy.waitForLoading();
|
||||
});
|
||||
|
||||
it("should display discount codes list page", () => {
|
||||
cy.contains("مدیریت کدهای تخفیف").should("be.visible");
|
||||
cy.getByTestId("create-discount-button").should("be.visible");
|
||||
});
|
||||
|
||||
it("should navigate to create discount code page", () => {
|
||||
cy.getByTestId("create-discount-button").click();
|
||||
cy.url().should("include", "/discount-codes/create");
|
||||
cy.contains("ایجاد کد تخفیف").should("be.visible");
|
||||
});
|
||||
|
||||
it("should create a basic percentage discount code", () => {
|
||||
cy.getByTestId("create-discount-button").click();
|
||||
|
||||
// Fill basic information using data-testid
|
||||
cy.getByTestId("discount-code-input").type("SAVE20");
|
||||
cy.getByTestId("discount-name-input").type("تخفیف ۲۰ درصدی");
|
||||
cy.getByTestId("discount-description-textarea").type(
|
||||
"تخفیف ۲۰ درصدی برای کل خرید"
|
||||
);
|
||||
|
||||
// Set discount settings using data-testid
|
||||
cy.getByTestId("discount-type-select").select("percentage");
|
||||
cy.getByTestId("discount-value-input").type("20");
|
||||
|
||||
// Set other required fields
|
||||
cy.getByTestId("discount-status-select").select("active");
|
||||
cy.getByTestId("discount-application-level-select").select("invoice");
|
||||
|
||||
// Submit form
|
||||
cy.getByTestId("submit-discount-button").click();
|
||||
|
||||
// Verify creation (might need to mock API response)
|
||||
cy.url().should("include", "/discount-codes");
|
||||
});
|
||||
|
||||
it("should validate required fields properly", () => {
|
||||
cy.getByTestId("create-discount-button").click();
|
||||
|
||||
// Submit button should be disabled initially
|
||||
cy.getByTestId("submit-discount-button").should("be.disabled");
|
||||
|
||||
// Fill only code field
|
||||
cy.getByTestId("discount-code-input").type("TEST");
|
||||
cy.getByTestId("submit-discount-button").should("be.disabled");
|
||||
|
||||
// Fill name field
|
||||
cy.getByTestId("discount-name-input").type("Test Name");
|
||||
cy.getByTestId("submit-discount-button").should("be.disabled");
|
||||
|
||||
// Fill all required fields
|
||||
cy.getByTestId("discount-type-select").select("percentage");
|
||||
cy.getByTestId("discount-value-input").type("10");
|
||||
cy.getByTestId("discount-status-select").select("active");
|
||||
cy.getByTestId("discount-application-level-select").select("invoice");
|
||||
|
||||
// Now submit button should be enabled
|
||||
cy.getByTestId("submit-discount-button").should("not.be.disabled");
|
||||
});
|
||||
|
||||
it("should validate code length constraints", () => {
|
||||
cy.getByTestId("create-discount-button").click();
|
||||
|
||||
// Test code too short
|
||||
cy.getByTestId("discount-code-input").type("AB");
|
||||
cy.getByTestId("discount-name-input").type("Test");
|
||||
cy.getByTestId("discount-type-select").select("percentage");
|
||||
cy.getByTestId("discount-value-input").type("10");
|
||||
|
||||
// Check for validation error
|
||||
cy.get(".text-red-600").should("contain", "کد باید حداقل ۳ کاراکتر باشد");
|
||||
|
||||
// Clear and test code too long
|
||||
cy.getByTestId("discount-code-input").clear().type("A".repeat(51));
|
||||
cy.get(".text-red-600").should(
|
||||
"contain",
|
||||
"کد نباید بیشتر از ۵۰ کاراکتر باشد"
|
||||
);
|
||||
});
|
||||
|
||||
it("should create different discount types", () => {
|
||||
const discountTypes = [
|
||||
{ type: "percentage", value: "25", level: "invoice" },
|
||||
{ type: "fixed", value: "50000", level: "invoice" },
|
||||
{ type: "fee_percentage", value: "5", level: "product_fee" },
|
||||
];
|
||||
|
||||
discountTypes.forEach((discount, index) => {
|
||||
// Navigate to create page before each iteration
|
||||
cy.visit("/discount-codes");
|
||||
cy.waitForLoading();
|
||||
cy.getByTestId("create-discount-button").click();
|
||||
|
||||
cy.getByTestId("discount-code-input").type(
|
||||
`TEST${index}${discount.type.toUpperCase()}`
|
||||
);
|
||||
cy.getByTestId("discount-name-input").type(`Test ${discount.type}`);
|
||||
cy.getByTestId("discount-type-select").select(discount.type);
|
||||
cy.getByTestId("discount-value-input").type(discount.value);
|
||||
cy.getByTestId("discount-status-select").select("active");
|
||||
cy.getByTestId("discount-application-level-select").select(
|
||||
discount.level
|
||||
);
|
||||
|
||||
cy.getByTestId("submit-discount-button").click();
|
||||
cy.url().should("include", "/discount-codes");
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle form cancellation", () => {
|
||||
cy.getByTestId("create-discount-button").click();
|
||||
|
||||
// Fill some data
|
||||
cy.getByTestId("discount-code-input").type("CANCELTEST");
|
||||
cy.getByTestId("discount-name-input").type("Cancel Test");
|
||||
|
||||
// Click cancel button
|
||||
cy.getByTestId("cancel-discount-button").click();
|
||||
|
||||
// Should return to list page
|
||||
cy.url().should("include", "/discount-codes");
|
||||
cy.url().should("not.include", "/create");
|
||||
});
|
||||
|
||||
it("should show empty state when no results found", () => {
|
||||
// Search for non-existent code
|
||||
cy.get('input[placeholder*="جستجو"]').type("NONEXISTENTCODE123");
|
||||
cy.wait(500);
|
||||
|
||||
// Check for empty state
|
||||
cy.contains("هیچ کد تخفیفی یافت نشد").should("be.visible");
|
||||
});
|
||||
|
||||
it("should navigate back properly", () => {
|
||||
cy.getByTestId("create-discount-button").click();
|
||||
|
||||
// Wait for form to load completely
|
||||
cy.getByTestId("discount-code-input").should("be.visible");
|
||||
|
||||
// Click cancel button
|
||||
cy.getByTestId("cancel-discount-button").click();
|
||||
|
||||
// Should return to list page
|
||||
cy.url().should("include", "/discount-codes");
|
||||
cy.url().should("not.include", "/create");
|
||||
});
|
||||
|
||||
// Test with API mocking
|
||||
it("should handle API errors gracefully", () => {
|
||||
// Mock API error
|
||||
cy.intercept("POST", "**/discount/**", {
|
||||
statusCode: 400,
|
||||
body: { message: "کد تخفیف تکراری است" },
|
||||
}).as("createError");
|
||||
|
||||
cy.getByTestId("create-discount-button").click();
|
||||
|
||||
cy.getByTestId("discount-code-input").type("ERRORTEST");
|
||||
cy.getByTestId("discount-name-input").type("Error Test");
|
||||
cy.getByTestId("discount-type-select").select("percentage");
|
||||
cy.getByTestId("discount-value-input").type("10");
|
||||
cy.getByTestId("discount-status-select").select("active");
|
||||
cy.getByTestId("discount-application-level-select").select("invoice");
|
||||
|
||||
cy.getByTestId("submit-discount-button").click();
|
||||
|
||||
cy.wait("@createError");
|
||||
// Error message should appear
|
||||
cy.contains("خطا در ایجاد کد تخفیف").should("be.visible");
|
||||
});
|
||||
|
||||
it("should handle loading states", () => {
|
||||
// Mock slow API response
|
||||
cy.intercept("POST", "**/discount/**", {
|
||||
delay: 2000,
|
||||
statusCode: 201,
|
||||
body: { id: 1, code: "TEST", name: "Test" },
|
||||
}).as("createSlow");
|
||||
|
||||
cy.getByTestId("create-discount-button").click();
|
||||
|
||||
cy.getByTestId("discount-code-input").type("LOADTEST");
|
||||
cy.getByTestId("discount-name-input").type("Load Test");
|
||||
cy.getByTestId("discount-type-select").select("percentage");
|
||||
cy.getByTestId("discount-value-input").type("10");
|
||||
cy.getByTestId("discount-status-select").select("active");
|
||||
cy.getByTestId("discount-application-level-select").select("invoice");
|
||||
|
||||
cy.getByTestId("submit-discount-button").click();
|
||||
|
||||
// Check loading state
|
||||
cy.getByTestId("submit-discount-button").should("be.disabled");
|
||||
|
||||
cy.wait("@createSlow");
|
||||
});
|
||||
|
||||
// Test mobile responsiveness
|
||||
it("should work on mobile viewport", () => {
|
||||
cy.viewport("iphone-6");
|
||||
|
||||
cy.getByTestId("create-discount-button").should("be.visible");
|
||||
cy.getByTestId("create-discount-button").click();
|
||||
|
||||
cy.contains("ایجاد کد تخفیف").should("be.visible");
|
||||
|
||||
// Form should be usable on mobile
|
||||
cy.getByTestId("discount-code-input").type("MOBILETEST");
|
||||
cy.getByTestId("discount-name-input").type("Mobile Test");
|
||||
cy.getByTestId("discount-type-select").select("percentage");
|
||||
cy.getByTestId("discount-value-input").type("10");
|
||||
cy.getByTestId("discount-status-select").select("active");
|
||||
cy.getByTestId("discount-application-level-select").select("invoice");
|
||||
|
||||
// Scroll to submit button to make it visible
|
||||
cy.getByTestId("submit-discount-button").scrollIntoView();
|
||||
cy.getByTestId("submit-discount-button").should("be.visible");
|
||||
});
|
||||
|
||||
// Test accessibility
|
||||
it("should be accessible", () => {
|
||||
cy.getByTestId("create-discount-button").click();
|
||||
|
||||
// Check for proper labels
|
||||
cy.get("label").should("have.length.greaterThan", 5);
|
||||
|
||||
// Check for required field indicators
|
||||
cy.getByTestId("discount-code-input").should(
|
||||
"have.attr",
|
||||
"aria-required",
|
||||
"true"
|
||||
);
|
||||
cy.getByTestId("discount-name-input").should(
|
||||
"have.attr",
|
||||
"aria-required",
|
||||
"true"
|
||||
);
|
||||
|
||||
// Check for proper form structure
|
||||
cy.get("form").should("exist");
|
||||
cy.get(".bg-gradient-to-r").should("have.length.greaterThan", 3);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,331 @@
|
|||
describe("Discount Codes Management", () => {
|
||||
beforeEach(() => {
|
||||
cy.login();
|
||||
cy.visit("/discount-codes");
|
||||
cy.waitForLoading();
|
||||
});
|
||||
|
||||
it("should display discount codes list page", () => {
|
||||
cy.contains("مدیریت کدهای تخفیف").should("be.visible");
|
||||
cy.contains("ایجاد و مدیریت کدهای تخفیف").should("be.visible");
|
||||
cy.get('[title="کد تخفیف جدید"]').should("be.visible");
|
||||
});
|
||||
|
||||
it("should navigate to create discount code page", () => {
|
||||
cy.get('[title="کد تخفیف جدید"]').click();
|
||||
cy.url().should("include", "/discount-codes/create");
|
||||
cy.contains("ایجاد کد تخفیف").should("be.visible");
|
||||
cy.contains("ایجاد و مدیریت کدهای تخفیف برای فروشگاه").should("be.visible");
|
||||
});
|
||||
|
||||
it("should create a percentage discount code", () => {
|
||||
cy.get('[title="کد تخفیف جدید"]').click();
|
||||
|
||||
// Fill basic information
|
||||
cy.get('input[name="code"]').type("SAVE20");
|
||||
cy.get('input[name="name"]').type("تخفیف ۲۰ درصدی");
|
||||
cy.get('textarea[name="description"]').type("تخفیف ۲۰ درصدی برای کل خرید");
|
||||
|
||||
// Set discount settings
|
||||
cy.get('select[name="type"]').select("percentage");
|
||||
cy.get('input[name="value"]').type("20");
|
||||
cy.get('select[name="status"]').select("active");
|
||||
cy.get('select[name="application_level"]').select("invoice");
|
||||
|
||||
// Set limits
|
||||
cy.get('input[name="min_purchase_amount"]').type("100000");
|
||||
cy.get('input[name="max_discount_amount"]').type("50000");
|
||||
cy.get('input[name="usage_limit"]').type("1000");
|
||||
cy.get('input[name="user_usage_limit"]').type("1");
|
||||
|
||||
// Set date range
|
||||
cy.get('input[name="valid_from"]').type("2024-01-01T00:00");
|
||||
cy.get('input[name="valid_to"]').type("2024-12-31T23:59");
|
||||
|
||||
// Set user restrictions
|
||||
cy.get('select[name="user_restrictions.user_group"]').select("loyal");
|
||||
|
||||
// Set meta information
|
||||
cy.get('input[name="meta.campaign"]').type("summer_sale");
|
||||
cy.get('input[name="meta.category"]').type("general");
|
||||
|
||||
// Submit form
|
||||
cy.get('button[type="submit"]').click();
|
||||
|
||||
// Verify creation
|
||||
cy.url().should("include", "/discount-codes");
|
||||
cy.contains("کد تخفیف با موفقیت ایجاد شد").should("be.visible");
|
||||
cy.contains("SAVE20").should("be.visible");
|
||||
});
|
||||
|
||||
it("should create a fixed amount discount code", () => {
|
||||
cy.get('[title="کد تخفیف جدید"]').click();
|
||||
|
||||
// Fill basic information
|
||||
cy.get('input[name="code"]').type("FIXED50000");
|
||||
cy.get('input[name="name"]').type("تخفیف ۵۰ هزار تومانی");
|
||||
cy.get('textarea[name="description"]').type(
|
||||
"تخفیف مبلغ ثابت ۵۰ هزار تومان"
|
||||
);
|
||||
|
||||
// Set discount settings
|
||||
cy.get('select[name="type"]').select("fixed");
|
||||
cy.get('input[name="value"]').type("50000");
|
||||
cy.get('select[name="status"]').select("active");
|
||||
cy.get('select[name="application_level"]').select("invoice");
|
||||
|
||||
// Set single use
|
||||
cy.get('input[name="single_use"]').check();
|
||||
|
||||
// Set user restrictions for new users only
|
||||
cy.get('input[name="user_restrictions.new_users_only"]').check();
|
||||
|
||||
// Submit form
|
||||
cy.get('button[type="submit"]').click();
|
||||
|
||||
// Verify creation
|
||||
cy.url().should("include", "/discount-codes");
|
||||
cy.contains("کد تخفیف با موفقیت ایجاد شد").should("be.visible");
|
||||
cy.contains("FIXED50000").should("be.visible");
|
||||
});
|
||||
|
||||
it("should create a fee percentage discount code", () => {
|
||||
cy.get('[title="کد تخفیف جدید"]').click();
|
||||
|
||||
// Fill basic information
|
||||
cy.get('input[name="code"]').type("FEEREDUCTION10");
|
||||
cy.get('input[name="name"]').type("کاهش کارمزد ۱۰ درصدی");
|
||||
|
||||
// Set discount settings
|
||||
cy.get('select[name="type"]').select("fee_percentage");
|
||||
cy.get('input[name="value"]').type("10");
|
||||
cy.get('select[name="application_level"]').select("product_fee");
|
||||
|
||||
// Submit form
|
||||
cy.get('button[type="submit"]').click();
|
||||
|
||||
// Verify creation
|
||||
cy.url().should("include", "/discount-codes");
|
||||
cy.contains("کد تخفیف با موفقیت ایجاد شد").should("be.visible");
|
||||
cy.contains("FEEREDUCTION10").should("be.visible");
|
||||
});
|
||||
|
||||
it("should validate required fields", () => {
|
||||
cy.get('[title="کد تخفیف جدید"]').click();
|
||||
|
||||
// Try to submit without required fields
|
||||
cy.get('button[type="submit"]').should("be.disabled");
|
||||
|
||||
// Fill only code field
|
||||
cy.get('input[name="code"]').type("TEST");
|
||||
cy.get('button[type="submit"]').should("be.disabled");
|
||||
|
||||
// Fill name field
|
||||
cy.get('input[name="name"]').type("Test");
|
||||
cy.get('button[type="submit"]').should("be.disabled");
|
||||
|
||||
// Fill all required fields
|
||||
cy.get('select[name="type"]').select("percentage");
|
||||
cy.get('input[name="value"]').type("10");
|
||||
cy.get('select[name="status"]').select("active");
|
||||
cy.get('select[name="application_level"]').select("invoice");
|
||||
|
||||
// Now submit button should be enabled
|
||||
cy.get('button[type="submit"]').should("not.be.disabled");
|
||||
});
|
||||
|
||||
it("should validate code length", () => {
|
||||
cy.get('[title="کد تخفیف جدید"]').click();
|
||||
|
||||
// Test code too short
|
||||
cy.get('input[name="code"]').type("AB");
|
||||
cy.get('input[name="name"]').type("Test");
|
||||
cy.get('select[name="type"]').select("percentage");
|
||||
cy.get('input[name="value"]').type("10");
|
||||
|
||||
cy.contains("کد باید حداقل ۳ کاراکتر باشد").should("be.visible");
|
||||
|
||||
// Clear and test code too long
|
||||
cy.get('input[name="code"]').clear().type("A".repeat(51));
|
||||
cy.contains("کد نباید بیشتر از ۵۰ کاراکتر باشد").should("be.visible");
|
||||
});
|
||||
|
||||
it("should validate percentage value range", () => {
|
||||
cy.get('[title="کد تخفیف جدید"]').click();
|
||||
|
||||
cy.get('input[name="code"]').type("TESTPERCENTAGE");
|
||||
cy.get('input[name="name"]').type("Test Percentage");
|
||||
cy.get('select[name="type"]').select("percentage");
|
||||
|
||||
// Test value too high for percentage (should warn in UI for >100)
|
||||
cy.get('input[name="value"]').type("150");
|
||||
|
||||
// The form should still accept it but backend will validate
|
||||
cy.get('select[name="status"]').select("active");
|
||||
cy.get('select[name="application_level"]').select("invoice");
|
||||
});
|
||||
|
||||
it("should search and filter discount codes", () => {
|
||||
// Assuming we have some discount codes in the list
|
||||
cy.get('input[placeholder="جستجو بر اساس کد..."]').type("SAVE");
|
||||
cy.wait(500); // Wait for search to filter
|
||||
|
||||
// Test status filter
|
||||
cy.get("select").contains("همه وضعیتها").parent().select("active");
|
||||
cy.wait(500);
|
||||
|
||||
// Clear filters
|
||||
cy.contains("پاک کردن فیلترها").click();
|
||||
cy.get('input[placeholder="جستجو بر اساس کد..."]').should("have.value", "");
|
||||
});
|
||||
|
||||
it("should edit existing discount code", () => {
|
||||
// Assuming we have discount codes in the list
|
||||
cy.get("table tbody tr")
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get('[title="ویرایش"]').click();
|
||||
});
|
||||
|
||||
cy.url().should("include", "/discount-codes/");
|
||||
cy.url().should("include", "/edit");
|
||||
cy.contains("ویرایش کد تخفیف").should("be.visible");
|
||||
|
||||
// Modify the discount code
|
||||
cy.get('input[name="name"]').clear().type("کد تخفیف ویرایش شده");
|
||||
cy.get('textarea[name="description"]').clear().type("توضیحات ویرایش شده");
|
||||
|
||||
// Submit changes
|
||||
cy.get('button[type="submit"]').click();
|
||||
|
||||
// Verify update
|
||||
cy.url().should("include", "/discount-codes");
|
||||
cy.contains("کد تخفیف با موفقیت بهروزرسانی شد").should("be.visible");
|
||||
cy.contains("کد تخفیف ویرایش شده").should("be.visible");
|
||||
});
|
||||
|
||||
it("should delete discount code", () => {
|
||||
// Create a test discount code first
|
||||
cy.get('[title="کد تخفیف جدید"]').click();
|
||||
cy.get('input[name="code"]').type("TESTDELETE");
|
||||
cy.get('input[name="name"]').type("Test Delete");
|
||||
cy.get('select[name="type"]').select("percentage");
|
||||
cy.get('input[name="value"]').type("10");
|
||||
cy.get('select[name="status"]').select("active");
|
||||
cy.get('select[name="application_level"]').select("invoice");
|
||||
cy.get('button[type="submit"]').click();
|
||||
|
||||
// Wait for creation
|
||||
cy.contains("کد تخفیف با موفقیت ایجاد شد").should("be.visible");
|
||||
|
||||
// Find and delete the test discount code
|
||||
cy.contains("TESTDELETE")
|
||||
.parent()
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.get('[title="حذف"]').click();
|
||||
});
|
||||
|
||||
// Confirm deletion in modal
|
||||
cy.contains("آیا از حذف این کد تخفیف اطمینان دارید؟").should("be.visible");
|
||||
cy.contains("button", "حذف").click();
|
||||
|
||||
// Verify deletion
|
||||
cy.contains("کد تخفیف با موفقیت حذف شد").should("be.visible");
|
||||
cy.contains("TESTDELETE").should("not.exist");
|
||||
});
|
||||
|
||||
it("should display proper status badges", () => {
|
||||
// Check if status badges are displayed with correct colors
|
||||
cy.get("table tbody tr").each(($row) => {
|
||||
cy.wrap($row).within(() => {
|
||||
cy.get("span")
|
||||
.contains(/فعال|غیرفعال/)
|
||||
.should("exist");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should show empty state when no discount codes exist", () => {
|
||||
// This test assumes a clean state or uses a filter that returns no results
|
||||
cy.get('input[placeholder="جستجو بر اساس کد..."]').type("NONEXISTENTCODE");
|
||||
cy.wait(500);
|
||||
|
||||
cy.contains("هیچ کد تخفیفی یافت نشد").should("be.visible");
|
||||
cy.contains("برای شروع یک کد تخفیف ایجاد کنید").should("be.visible");
|
||||
});
|
||||
|
||||
it("should cancel discount code creation", () => {
|
||||
cy.get('[title="کد تخفیف جدید"]').click();
|
||||
|
||||
// Fill some data
|
||||
cy.get('input[name="code"]').type("CANCELTEST");
|
||||
cy.get('input[name="name"]').type("Cancel Test");
|
||||
|
||||
// Click cancel
|
||||
cy.contains("button", "انصراف").click();
|
||||
|
||||
// Should return to list page
|
||||
cy.url().should("include", "/discount-codes");
|
||||
cy.url().should("not.include", "/create");
|
||||
});
|
||||
|
||||
it("should handle user restrictions properly", () => {
|
||||
cy.get('[title="کد تخفیف جدید"]').click();
|
||||
|
||||
// Fill basic fields
|
||||
cy.get('input[name="code"]').type("USERRESTRICT");
|
||||
cy.get('input[name="name"]').type("User Restriction Test");
|
||||
cy.get('select[name="type"]').select("percentage");
|
||||
cy.get('input[name="value"]').type("15");
|
||||
cy.get('select[name="status"]').select("active");
|
||||
cy.get('select[name="application_level"]').select("invoice");
|
||||
|
||||
// Set user restrictions
|
||||
cy.get('select[name="user_restrictions.user_group"]').select("new");
|
||||
cy.get('input[name="user_restrictions.min_purchase_count"]').type("0");
|
||||
cy.get('input[name="user_restrictions.max_purchase_count"]').type("5");
|
||||
cy.get('input[name="user_restrictions.referrer_user_id"]').type("123");
|
||||
|
||||
// Check warning about mutually exclusive options
|
||||
cy.get('input[name="user_restrictions.new_users_only"]').check();
|
||||
cy.get('input[name="user_restrictions.loyal_users_only"]').check();
|
||||
|
||||
// Should show warning
|
||||
cy.contains(
|
||||
"new_users_only و loyal_users_only نمیتوانند همزمان فعال باشند"
|
||||
).should("be.visible");
|
||||
|
||||
cy.get('button[type="submit"]').click();
|
||||
|
||||
cy.url().should("include", "/discount-codes");
|
||||
cy.contains("کد تخفیف با موفقیت ایجاد شد").should("be.visible");
|
||||
});
|
||||
|
||||
it("should validate form sections are properly organized", () => {
|
||||
cy.get('[title="کد تخفیف جدید"]').click();
|
||||
|
||||
// Verify all sections exist
|
||||
cy.contains("اطلاعات اصلی کد تخفیف").should("be.visible");
|
||||
cy.contains("تنظیمات تخفیف").should("be.visible");
|
||||
cy.contains("بازه زمانی اعتبار").should("be.visible");
|
||||
cy.contains("محدودیتهای کاربری").should("be.visible");
|
||||
cy.contains("اطلاعات تکمیلی").should("be.visible");
|
||||
|
||||
// Verify form has proper styling
|
||||
cy.get(".bg-gradient-to-r").should("have.length.greaterThan", 3);
|
||||
cy.get(".rounded-xl").should("have.length.greaterThan", 3);
|
||||
});
|
||||
|
||||
it("should handle back navigation properly", () => {
|
||||
cy.get('[title="کد تخفیف جدید"]').click();
|
||||
|
||||
// Click back button
|
||||
cy.contains("بازگشت").click();
|
||||
|
||||
// Should return to list page
|
||||
cy.url().should("include", "/discount-codes");
|
||||
cy.url().should("not.include", "/create");
|
||||
cy.contains("مدیریت کدهای تخفیف").should("be.visible");
|
||||
});
|
||||
});
|
||||
|
|
@ -16,6 +16,12 @@ describe("Smoke Tests", () => {
|
|||
cy.visit("/products");
|
||||
cy.url().should("include", "/products");
|
||||
|
||||
cy.visit("/discount-codes");
|
||||
cy.url().should("include", "/discount-codes");
|
||||
|
||||
cy.visit("/orders");
|
||||
cy.url().should("include", "/orders");
|
||||
|
||||
cy.visit("/admin-users");
|
||||
cy.url().should("include", "/admin-users");
|
||||
|
||||
|
|
|
|||
|
|
@ -31,3 +31,6 @@ Cypress.Commands.add("waitForLoading", () => {
|
|||
// Wait for any loading spinner to disappear
|
||||
cy.get(".animate-spin", { timeout: 1000 }).should("not.exist");
|
||||
});
|
||||
|
||||
// Import discount codes helpers
|
||||
import "./discount-codes-helpers";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,310 @@
|
|||
// Helper functions for discount codes E2E tests
|
||||
|
||||
export interface DiscountCodeData {
|
||||
code: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
type: "percentage" | "fixed" | "fee_percentage";
|
||||
value: string;
|
||||
status: "active" | "inactive";
|
||||
applicationLevel:
|
||||
| "invoice"
|
||||
| "category"
|
||||
| "product"
|
||||
| "shipping"
|
||||
| "product_fee";
|
||||
minPurchaseAmount?: string;
|
||||
maxDiscountAmount?: string;
|
||||
usageLimit?: string;
|
||||
userUsageLimit?: string;
|
||||
singleUse?: boolean;
|
||||
validFrom?: string;
|
||||
validTo?: string;
|
||||
userGroup?: "new" | "loyal" | "all";
|
||||
newUsersOnly?: boolean;
|
||||
loyalUsersOnly?: boolean;
|
||||
campaign?: string;
|
||||
category?: string;
|
||||
}
|
||||
|
||||
declare global {
|
||||
namespace Cypress {
|
||||
interface Chainable {
|
||||
createDiscountCode(data: DiscountCodeData): Chainable<void>;
|
||||
fillBasicDiscountInfo(data: Partial<DiscountCodeData>): Chainable<void>;
|
||||
fillDiscountSettings(data: Partial<DiscountCodeData>): Chainable<void>;
|
||||
fillUserRestrictions(data: Partial<DiscountCodeData>): Chainable<void>;
|
||||
submitDiscountForm(): Chainable<void>;
|
||||
verifyDiscountCreation(): Chainable<void>;
|
||||
navigateToCreateDiscount(): Chainable<void>;
|
||||
searchDiscountCode(code: string): Chainable<void>;
|
||||
clearDiscountFilters(): Chainable<void>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Navigate to create discount page
|
||||
Cypress.Commands.add("navigateToCreateDiscount", () => {
|
||||
cy.visit("/discount-codes");
|
||||
cy.waitForLoading();
|
||||
cy.getByTestId("create-discount-button").click();
|
||||
cy.url().should("include", "/discount-codes/create");
|
||||
});
|
||||
|
||||
// Fill basic discount information
|
||||
Cypress.Commands.add(
|
||||
"fillBasicDiscountInfo",
|
||||
(data: Partial<DiscountCodeData>) => {
|
||||
if (data.code) {
|
||||
cy.getByTestId("discount-code-input").clear().type(data.code);
|
||||
}
|
||||
if (data.name) {
|
||||
cy.getByTestId("discount-name-input").clear().type(data.name);
|
||||
}
|
||||
if (data.description) {
|
||||
cy.getByTestId("discount-description-textarea")
|
||||
.clear()
|
||||
.type(data.description);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Fill discount settings
|
||||
Cypress.Commands.add(
|
||||
"fillDiscountSettings",
|
||||
(data: Partial<DiscountCodeData>) => {
|
||||
if (data.type) {
|
||||
cy.getByTestId("discount-type-select").select(data.type);
|
||||
}
|
||||
if (data.value) {
|
||||
cy.getByTestId("discount-value-input").clear().type(data.value);
|
||||
}
|
||||
if (data.status) {
|
||||
cy.getByTestId("discount-status-select").select(data.status);
|
||||
}
|
||||
if (data.applicationLevel) {
|
||||
cy.getByTestId("discount-application-level-select").select(
|
||||
data.applicationLevel
|
||||
);
|
||||
}
|
||||
if (data.minPurchaseAmount) {
|
||||
cy.get('input[name="min_purchase_amount"]')
|
||||
.clear()
|
||||
.type(data.minPurchaseAmount);
|
||||
}
|
||||
if (data.maxDiscountAmount) {
|
||||
cy.get('input[name="max_discount_amount"]')
|
||||
.clear()
|
||||
.type(data.maxDiscountAmount);
|
||||
}
|
||||
if (data.usageLimit) {
|
||||
cy.get('input[name="usage_limit"]').clear().type(data.usageLimit);
|
||||
}
|
||||
if (data.userUsageLimit) {
|
||||
cy.get('input[name="user_usage_limit"]')
|
||||
.clear()
|
||||
.type(data.userUsageLimit);
|
||||
}
|
||||
if (data.singleUse) {
|
||||
cy.get('input[name="single_use"]').check();
|
||||
}
|
||||
if (data.validFrom) {
|
||||
cy.get('input[name="valid_from"]').type(data.validFrom);
|
||||
}
|
||||
if (data.validTo) {
|
||||
cy.get('input[name="valid_to"]').type(data.validTo);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Fill user restrictions
|
||||
Cypress.Commands.add(
|
||||
"fillUserRestrictions",
|
||||
(data: Partial<DiscountCodeData>) => {
|
||||
if (data.userGroup) {
|
||||
cy.get('select[name="user_restrictions.user_group"]').select(
|
||||
data.userGroup
|
||||
);
|
||||
}
|
||||
if (data.newUsersOnly) {
|
||||
cy.get('input[name="user_restrictions.new_users_only"]').check();
|
||||
}
|
||||
if (data.loyalUsersOnly) {
|
||||
cy.get('input[name="user_restrictions.loyal_users_only"]').check();
|
||||
}
|
||||
if (data.campaign) {
|
||||
cy.get('input[name="meta.campaign"]').clear().type(data.campaign);
|
||||
}
|
||||
if (data.category) {
|
||||
cy.get('input[name="meta.category"]').clear().type(data.category);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Submit discount form
|
||||
Cypress.Commands.add("submitDiscountForm", () => {
|
||||
cy.getByTestId("submit-discount-button").click();
|
||||
});
|
||||
|
||||
// Verify discount creation
|
||||
Cypress.Commands.add("verifyDiscountCreation", () => {
|
||||
cy.url().should("include", "/discount-codes");
|
||||
cy.url().should("not.include", "/create");
|
||||
cy.url().should("not.include", "/edit");
|
||||
});
|
||||
|
||||
// Create complete discount code
|
||||
Cypress.Commands.add("createDiscountCode", (data: DiscountCodeData) => {
|
||||
cy.navigateToCreateDiscount();
|
||||
cy.fillBasicDiscountInfo(data);
|
||||
cy.fillDiscountSettings(data);
|
||||
cy.fillUserRestrictions(data);
|
||||
cy.submitDiscountForm();
|
||||
cy.verifyDiscountCreation();
|
||||
});
|
||||
|
||||
// Search for discount code
|
||||
Cypress.Commands.add("searchDiscountCode", (code: string) => {
|
||||
cy.get('input[placeholder*="جستجو"]').clear().type(code);
|
||||
cy.wait(500); // Wait for search to filter
|
||||
});
|
||||
|
||||
// Clear discount filters
|
||||
Cypress.Commands.add("clearDiscountFilters", () => {
|
||||
cy.contains("پاک کردن فیلترها").click();
|
||||
cy.get('input[placeholder*="جستجو"]').should("have.value", "");
|
||||
});
|
||||
|
||||
// Predefined discount code templates for testing
|
||||
export const discountTemplates = {
|
||||
basicPercentage: {
|
||||
code: "BASIC20",
|
||||
name: "Basic 20% Discount",
|
||||
description: "Basic percentage discount for testing",
|
||||
type: "percentage" as const,
|
||||
value: "20",
|
||||
status: "active" as const,
|
||||
applicationLevel: "invoice" as const,
|
||||
},
|
||||
|
||||
fixedAmount: {
|
||||
code: "FIXED50K",
|
||||
name: "Fixed 50K Discount",
|
||||
description: "Fixed amount discount for testing",
|
||||
type: "fixed" as const,
|
||||
value: "50000",
|
||||
status: "active" as const,
|
||||
applicationLevel: "invoice" as const,
|
||||
minPurchaseAmount: "100000",
|
||||
},
|
||||
|
||||
feePercentage: {
|
||||
code: "FEERED10",
|
||||
name: "Fee Reduction 10%",
|
||||
description: "Fee percentage reduction for testing",
|
||||
type: "fee_percentage" as const,
|
||||
value: "10",
|
||||
status: "active" as const,
|
||||
applicationLevel: "product_fee" as const,
|
||||
},
|
||||
|
||||
loyalUsers: {
|
||||
code: "LOYAL25",
|
||||
name: "Loyal Users 25%",
|
||||
description: "Discount for loyal users only",
|
||||
type: "percentage" as const,
|
||||
value: "25",
|
||||
status: "active" as const,
|
||||
applicationLevel: "invoice" as const,
|
||||
userGroup: "loyal" as const,
|
||||
loyalUsersOnly: true,
|
||||
},
|
||||
|
||||
newUsers: {
|
||||
code: "WELCOME15",
|
||||
name: "Welcome New Users",
|
||||
description: "Welcome discount for new users",
|
||||
type: "percentage" as const,
|
||||
value: "15",
|
||||
status: "active" as const,
|
||||
applicationLevel: "invoice" as const,
|
||||
userGroup: "new" as const,
|
||||
newUsersOnly: true,
|
||||
singleUse: true,
|
||||
},
|
||||
|
||||
timeBasedDiscount: {
|
||||
code: "SUMMER24",
|
||||
name: "Summer Sale 2024",
|
||||
description: "Summer sale discount with time constraints",
|
||||
type: "percentage" as const,
|
||||
value: "30",
|
||||
status: "active" as const,
|
||||
applicationLevel: "invoice" as const,
|
||||
validFrom: "2024-06-01T00:00",
|
||||
validTo: "2024-08-31T23:59",
|
||||
usageLimit: "1000",
|
||||
userUsageLimit: "1",
|
||||
campaign: "summer_sale_2024",
|
||||
category: "seasonal",
|
||||
},
|
||||
};
|
||||
|
||||
// API response mocks
|
||||
export const apiMocks = {
|
||||
successfulCreation: (data: Partial<DiscountCodeData>) => ({
|
||||
statusCode: 201,
|
||||
body: {
|
||||
id: Math.floor(Math.random() * 1000),
|
||||
code: data.code,
|
||||
name: data.name,
|
||||
description: data.description,
|
||||
type: data.type,
|
||||
value: parseFloat(data.value || "0"),
|
||||
status: data.status,
|
||||
application_level: data.applicationLevel,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
},
|
||||
}),
|
||||
|
||||
validationError: {
|
||||
statusCode: 400,
|
||||
body: {
|
||||
message: "کد تخفیف تکراری است",
|
||||
errors: {
|
||||
code: ["این کد قبلاً استفاده شده است"],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
serverError: {
|
||||
statusCode: 500,
|
||||
body: {
|
||||
message: "خطای سرور",
|
||||
},
|
||||
},
|
||||
|
||||
discountsList: {
|
||||
statusCode: 200,
|
||||
body: {
|
||||
discount_codes: [
|
||||
{
|
||||
id: 1,
|
||||
code: "SAVE20",
|
||||
name: "20% Off Discount",
|
||||
description: "Get 20% off on your purchase",
|
||||
type: "percentage",
|
||||
value: 20,
|
||||
status: "active",
|
||||
application_level: "invoice",
|
||||
created_at: "2024-01-01T00:00:00Z",
|
||||
},
|
||||
],
|
||||
total: 1,
|
||||
page: 1,
|
||||
limit: 20,
|
||||
total_pages: 1,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
login(username?: string, password?: string): Chainable<void>;
|
||||
logout(): Chainable<void>;
|
||||
getByTestId(testId: string): Chainable<JQuery<HTMLElement>>;
|
||||
waitForLoading(): Chainable<void>;
|
||||
|
||||
// Discount codes helper methods
|
||||
navigateToCreateDiscount(): Chainable<void>;
|
||||
fillBasicDiscountInfo(data: any): Chainable<void>;
|
||||
fillDiscountSettings(data: any): Chainable<void>;
|
||||
fillUserRestrictions(data: any): Chainable<void>;
|
||||
submitDiscountForm(): Chainable<void>;
|
||||
verifyDiscountCreation(): Chainable<void>;
|
||||
createDiscountCode(data: any): Chainable<void>;
|
||||
searchDiscountCode(code: string): Chainable<void>;
|
||||
clearDiscountFilters(): Chainable<void>;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
|
@ -1,7 +1,7 @@
|
|||
import { clsx } from 'clsx';
|
||||
import { MouseEvent } from 'react';
|
||||
import { MouseEvent, ButtonHTMLAttributes } from 'react';
|
||||
|
||||
interface ButtonProps {
|
||||
interface ButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'type' | 'onClick'> {
|
||||
children: any;
|
||||
variant?: 'primary' | 'secondary' | 'danger' | 'success';
|
||||
size?: 'sm' | 'md' | 'lg';
|
||||
|
|
@ -21,6 +21,7 @@ export const Button = ({
|
|||
onClick,
|
||||
type = 'button',
|
||||
className = '',
|
||||
...rest
|
||||
}: ButtonProps) => {
|
||||
const baseClasses = 'inline-flex items-center justify-center rounded-lg font-medium transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2';
|
||||
|
||||
|
|
@ -53,6 +54,7 @@ export const Button = ({
|
|||
disabledClasses,
|
||||
className
|
||||
)}
|
||||
{...rest}
|
||||
>
|
||||
{loading && (
|
||||
<svg
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ export const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|||
<p className="text-xs text-gray-500 dark:text-gray-400">{helperText}</p>
|
||||
)}
|
||||
{error && (
|
||||
<p className="text-xs text-red-600 dark:text-red-400">{error}</p>
|
||||
<p className="text-xs text-red-600 dark:text-red-400" role="alert">{error}</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -127,6 +127,9 @@ const DiscountCodeFormPage = () => {
|
|||
error={errors.code?.message}
|
||||
{...register('code')}
|
||||
className="font-mono"
|
||||
data-testid="discount-code-input"
|
||||
aria-required="true"
|
||||
required
|
||||
/>
|
||||
<Input
|
||||
label="نام نمایشی"
|
||||
|
|
@ -134,6 +137,9 @@ const DiscountCodeFormPage = () => {
|
|||
placeholder="مثال: تخفیف ۲۰ درصدی"
|
||||
error={errors.name?.message}
|
||||
{...register('name')}
|
||||
data-testid="discount-name-input"
|
||||
aria-required="true"
|
||||
required
|
||||
/>
|
||||
<div className="lg:col-span-2">
|
||||
<Label htmlFor="description">توضیحات</Label>
|
||||
|
|
@ -142,6 +148,7 @@ const DiscountCodeFormPage = () => {
|
|||
placeholder="توضیحات کامل درباره این کد تخفیف..."
|
||||
className={`w-full px-4 py-3 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 resize-none h-24 transition-colors ${errors.description ? 'border-red-500 focus:ring-red-500 focus:border-red-500' : 'border-gray-300 dark:border-gray-600'} dark:bg-gray-700 dark:text-gray-100`}
|
||||
{...register('description' as const)}
|
||||
data-testid="discount-description-textarea"
|
||||
/>
|
||||
{errors.description && <p className="text-sm text-red-600 dark:text-red-400 mt-1">{errors.description.message as string}</p>}
|
||||
</div>
|
||||
|
|
@ -163,7 +170,11 @@ const DiscountCodeFormPage = () => {
|
|||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<div className="space-y-2">
|
||||
<Label>نوع تخفیف</Label>
|
||||
<select className="w-full px-4 py-3 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-gray-100 transition-colors" {...register('type')}>
|
||||
<select
|
||||
className="w-full px-4 py-3 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-gray-100 transition-colors"
|
||||
{...register('type')}
|
||||
data-testid="discount-type-select"
|
||||
>
|
||||
<option value="percentage">درصدی</option>
|
||||
<option value="fixed">مبلغ ثابت</option>
|
||||
<option value="fee_percentage">درصد کارمزد</option>
|
||||
|
|
@ -177,25 +188,38 @@ const DiscountCodeFormPage = () => {
|
|||
placeholder="20"
|
||||
error={errors.value?.message as string}
|
||||
{...register('value')}
|
||||
data-testid="discount-value-input"
|
||||
/>
|
||||
<div className="space-y-2">
|
||||
<Label>وضعیت</Label>
|
||||
<select className="w-full px-4 py-3 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-gray-100 transition-colors" {...register('status')}>
|
||||
<select
|
||||
className="w-full px-4 py-3 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-gray-100 transition-colors"
|
||||
{...register('status')}
|
||||
data-testid="discount-status-select"
|
||||
required
|
||||
aria-required="true"
|
||||
>
|
||||
<option value="active">فعال</option>
|
||||
<option value="inactive">غیرفعال</option>
|
||||
</select>
|
||||
{errors.status && <p className="text-sm text-red-600 dark:text-red-400">{errors.status.message as string}</p>}
|
||||
{errors.status && <p className="text-sm text-red-600 dark:text-red-400" role="alert">{errors.status.message as string}</p>}
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>سطح اعمال</Label>
|
||||
<select className="w-full px-4 py-3 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-gray-100 transition-colors" {...register('application_level')}>
|
||||
<select
|
||||
className="w-full px-4 py-3 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-gray-100 transition-colors"
|
||||
{...register('application_level')}
|
||||
data-testid="discount-application-level-select"
|
||||
required
|
||||
aria-required="true"
|
||||
>
|
||||
<option value="invoice">کل سبد خرید</option>
|
||||
<option value="category">دستهبندی خاص</option>
|
||||
<option value="product">محصول خاص</option>
|
||||
<option value="shipping">هزینه ارسال</option>
|
||||
<option value="product_fee">کارمزد محصول</option>
|
||||
</select>
|
||||
{errors.application_level && <p className="text-sm text-red-600 dark:text-red-400">{errors.application_level.message as string}</p>}
|
||||
{errors.application_level && <p className="text-sm text-red-600 dark:text-red-400" role="alert">{errors.application_level.message as string}</p>}
|
||||
</div>
|
||||
<Input
|
||||
label="حداقل مبلغ خرید"
|
||||
|
|
@ -352,6 +376,7 @@ const DiscountCodeFormPage = () => {
|
|||
variant="secondary"
|
||||
onClick={() => navigate('/discount-codes')}
|
||||
className="sm:order-1"
|
||||
data-testid="cancel-discount-button"
|
||||
>
|
||||
انصراف
|
||||
</Button>
|
||||
|
|
@ -361,6 +386,7 @@ const DiscountCodeFormPage = () => {
|
|||
loading={isLoading}
|
||||
disabled={!isValid}
|
||||
className="sm:order-2 bg-gradient-to-r from-blue-600 to-blue-700 hover:from-blue-700 hover:to-blue-800"
|
||||
data-testid="submit-discount-button"
|
||||
>
|
||||
{isEdit ? 'بهروزرسانی کد تخفیف' : 'ایجاد کد تخفیف'}
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ const DiscountCodesListPage = () => {
|
|||
onClick={handleCreate}
|
||||
className="flex items-center justify-center w-12 h-12 bg-primary-600 hover:bg-primary-700 rounded-full transition-colors duration-200 text-white shadow-lg hover:shadow-xl"
|
||||
title="کد تخفیف جدید"
|
||||
data-testid="create-discount-button"
|
||||
>
|
||||
<Plus className="h-5 w-5" />
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
// User Admin models and types
|
||||
|
||||
export interface User {
|
||||
id: number;
|
||||
phone_number: string;
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
email?: string;
|
||||
national_code?: string;
|
||||
verified: boolean;
|
||||
hashed_password?: string;
|
||||
avatar?: string;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
}
|
||||
|
||||
export interface PaginatedUsersResponse {
|
||||
users: User[];
|
||||
total: number;
|
||||
limit: number;
|
||||
offset: number;
|
||||
filters?: UserFilters;
|
||||
}
|
||||
|
||||
export interface UserFilters {
|
||||
verified?: boolean;
|
||||
search_text?: string;
|
||||
phone_number?: string;
|
||||
email?: string;
|
||||
national_code?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
}
|
||||
|
||||
export interface CreateUserRequest {
|
||||
phone_number: string;
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
email?: string;
|
||||
national_code?: string;
|
||||
verified?: boolean;
|
||||
password?: string;
|
||||
}
|
||||
|
||||
export interface UpdateUserRequest {
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
email?: string;
|
||||
national_code?: string;
|
||||
verified: boolean;
|
||||
}
|
||||
|
||||
export interface UpdateUserProfileRequest {
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
email?: string;
|
||||
national_code?: string;
|
||||
}
|
||||
|
||||
export interface UpdateUserAvatarRequest {
|
||||
avatar_url: string;
|
||||
}
|
||||
|
||||
export interface UserStats {
|
||||
total_users: number;
|
||||
verified_users: number;
|
||||
unverified_users: number;
|
||||
recent_registrations: number;
|
||||
}
|
||||
|
||||
export type UserStatus = 'verified' | 'unverified' | 'all';
|
||||
|
||||
export interface UserActionResponse {
|
||||
message: string;
|
||||
}
|
||||
Loading…
Reference in New Issue