diff --git a/cypress.config.ts b/cypress.config.ts new file mode 100644 index 0000000..9ed9173 --- /dev/null +++ b/cypress.config.ts @@ -0,0 +1,27 @@ +import { defineConfig } from "cypress"; + +export default defineConfig({ + e2e: { + baseUrl: "http://localhost:5173", + setupNodeEvents(on, config) { + // implement node event listeners here + }, + specPattern: "cypress/e2e/**/*.cy.{js,jsx,ts,tsx}", + supportFile: "cypress/support/e2e.ts", + viewportWidth: 1280, + viewportHeight: 720, + video: true, + screenshotOnRunFailure: true, + defaultCommandTimeout: 10000, + requestTimeout: 10000, + responseTimeout: 10000, + }, + component: { + devServer: { + framework: "react", + bundler: "vite", + }, + specPattern: "cypress/component/**/*.cy.{js,jsx,ts,tsx}", + supportFile: "cypress/support/component.ts", + }, +}); diff --git a/cypress/e2e/auth.cy.ts b/cypress/e2e/auth.cy.ts new file mode 100644 index 0000000..9662a8b --- /dev/null +++ b/cypress/e2e/auth.cy.ts @@ -0,0 +1,106 @@ +describe("Authentication", () => { + beforeEach(() => { + cy.visit("/login"); + }); + + it("should display login form", () => { + cy.get('input[name="username"]').should("be.visible"); + cy.get('input[name="password"]').should("be.visible"); + cy.get('button[type="submit"]').should("be.visible"); + cy.contains("ورود به پنل مدیریت").should("be.visible"); + cy.contains("لطفا اطلاعات خود را وارد کنید").should("be.visible"); + }); + + it("should show validation errors for empty fields", () => { + // Type something then clear to trigger validation + cy.get('input[name="username"]').type("a").clear(); + cy.get('input[name="password"]').type("a").clear(); + + // Click outside to trigger validation + cy.get("body").click(); + + cy.contains("نام کاربری الزامی است").should("be.visible"); + cy.contains("رمز عبور الزامی است").should("be.visible"); + }); + + it("should show error for invalid credentials", () => { + cy.get('input[name="username"]').type("invaliduser"); + cy.get('input[name="password"]').type("wrongpass"); + cy.get('button[type="submit"]').click(); + + cy.contains("نام کاربری یا رمز عبور اشتباه است", { timeout: 10000 }).should( + "be.visible" + ); + }); + + it("should successfully login with valid credentials", () => { + cy.get('input[name="username"]').type("admin"); + cy.get('input[name="password"]').type("admin123"); + cy.get('button[type="submit"]').click(); + + // Should redirect to dashboard - handle trailing slash + cy.url().should("not.include", "/login"); + cy.url().should("satisfy", (url) => { + return ( + url === Cypress.config().baseUrl || + url === Cypress.config().baseUrl + "/" + ); + }); + + // Should see dashboard content + cy.contains("داشبورد").should("be.visible"); + }); + + it("should logout successfully", () => { + // First login + cy.get('input[name="username"]').type("admin"); + cy.get('input[name="password"]').type("admin123"); + cy.get('button[type="submit"]').click(); + cy.url().should("not.include", "/login"); + + // Clear session to simulate logout + cy.clearLocalStorage(); + cy.visit("/login"); + + // Should redirect to login + cy.url().should("include", "/login"); + cy.contains("ورود به پنل مدیریت").should("be.visible"); + }); + + it("should redirect to login when accessing protected routes without authentication", () => { + cy.visit("/products"); + cy.url().should("include", "/login"); + + cy.visit("/admin-users"); + cy.url().should("include", "/login"); + + cy.visit("/roles"); + cy.url().should("include", "/login"); + }); + + it("should remember login state after page refresh", () => { + // Login first + cy.get('input[name="username"]').type("admin"); + cy.get('input[name="password"]').type("admin123"); + cy.get('button[type="submit"]').click(); + cy.url().should("not.include", "/login"); + + cy.reload(); + + // Should still be logged in + cy.url().should("not.include", "/login"); + cy.contains("داشبورد").should("be.visible"); + }); + + it("should toggle password visibility", () => { + cy.get('input[name="password"]').should("have.attr", "type", "password"); + + // Click the eye button to show password + cy.get(".absolute.inset-y-0.left-0").click(); + cy.get('input[name="password"]').should("have.attr", "type", "text"); + + // Click again to hide password + cy.get(".absolute.inset-y-0.left-0").click(); + cy.get('input[name="password"]').should("have.attr", "type", "password"); + }); +}); diff --git a/cypress/e2e/categories-advanced.cy.ts b/cypress/e2e/categories-advanced.cy.ts new file mode 100644 index 0000000..ef980ba --- /dev/null +++ b/cypress/e2e/categories-advanced.cy.ts @@ -0,0 +1,211 @@ +describe("Categories - Advanced Tests", () => { + beforeEach(() => { + cy.login(); + }); + + describe("Category CRUD Operations", () => { + it("should create a new category", () => { + cy.visit("/categories"); + cy.get(".bg-primary-600.rounded-full").first().click(); + + // Fill category information + cy.get('input[name="name"]').type("دسته‌بندی تست"); + cy.get('textarea[name="description"]').type("توضیحات دسته‌بندی تست"); + cy.get('input[name="sort_order"]').clear().type("1"); + + // Enable category + cy.get('input[name="enabled"]').check({ force: true }); + + // Submit form + cy.get('button[type="submit"]').click(); + + // Verify redirect and success + cy.url().should("include", "/categories"); + cy.contains("دسته‌بندی تست").should("be.visible"); + }); + + it("should edit an existing category", () => { + cy.visit("/categories"); + + // Click edit on first category + cy.get("tbody tr") + .first() + .within(() => { + cy.get( + '[data-testid="edit-button"], [title="ویرایش"], .text-blue-600' + ) + .first() + .click(); + }); + + // Update category name + cy.get('input[name="name"]').clear().type("دسته‌بندی ویرایش شده"); + cy.get('button[type="submit"]').click(); + + // Verify changes + cy.url().should("include", "/categories"); + cy.contains("دسته‌بندی ویرایش شده").should("be.visible"); + }); + + it("should delete a category with confirmation", () => { + cy.visit("/categories"); + + // Click delete on first category + cy.get("tbody tr") + .first() + .within(() => { + cy.get('[data-testid="delete-button"], [title="حذف"], .text-red-600') + .first() + .click(); + }); + + // Confirm deletion in modal + cy.get('.modal, [role="dialog"]').should("be.visible"); + cy.get("button").contains("حذف").click(); + + // Verify success message + cy.contains("دسته‌بندی با موفقیت حذف شد", { timeout: 10000 }).should( + "be.visible" + ); + }); + }); + + describe("Category Form Validation", () => { + beforeEach(() => { + cy.visit("/categories"); + cy.get(".bg-primary-600.rounded-full").first().click(); + }); + + it("should show validation errors for empty required fields", () => { + // Try to submit empty form + cy.get('button[type="submit"]').click(); + + // Check for validation messages + cy.contains("نام دسته‌بندی الزامی است", { timeout: 5000 }).should( + "be.visible" + ); + }); + + it("should validate minimum length for category name", () => { + cy.get('input[name="name"]').type("a"); + cy.get('button[type="submit"]').click(); + + cy.contains("نام دسته‌بندی باید حداقل", { timeout: 5000 }).should( + "be.visible" + ); + }); + + it("should validate sort order is a number", () => { + cy.get('input[name="name"]').type("دسته‌بندی تست"); + cy.get('input[name="sort_order"]').clear().type("abc"); + cy.get('button[type="submit"]').click(); + + cy.contains("ترتیب نمایش باید عدد باشد").should("be.visible"); + }); + }); + + describe("Category Search and Filter", () => { + beforeEach(() => { + cy.visit("/categories"); + }); + + it("should search categories by name", () => { + cy.get('input[placeholder*="جستجو"], input[name="search"]').type("دسته"); + cy.get('button[type="submit"], button').contains("جستجو").click(); + + // Wait for results + cy.wait(2000); + + // Check search results + cy.get("tbody tr").should("have.length.at.least", 0); + }); + + it("should filter categories by status", () => { + cy.get('select[name="enabled"], select').first().select("true"); + cy.get("button").contains("اعمال فیلتر").click(); + + cy.wait(2000); + + // Results should be filtered + cy.get("tbody tr").should("have.length.at.least", 0); + }); + }); + + describe("Category Status Management", () => { + beforeEach(() => { + cy.visit("/categories"); + }); + + it("should toggle category status", () => { + cy.get("tbody tr") + .first() + .within(() => { + cy.get('input[type="checkbox"], .toggle') + .first() + .click({ force: true }); + }); + + cy.contains("وضعیت دسته‌بندی با موفقیت تغییر کرد").should("be.visible"); + }); + }); + + describe("Category Image Upload", () => { + beforeEach(() => { + cy.visit("/categories"); + cy.get(".bg-primary-600.rounded-full").first().click(); + }); + + it("should upload category image", () => { + cy.get('input[name="name"]').type("دسته‌بندی با تصویر"); + + // Upload image + cy.get('input[type="file"]').selectFile( + "cypress/fixtures/category-image.jpg", + { force: true } + ); + + // Wait for upload + cy.wait(2000); + + cy.get('button[type="submit"]').click(); + + // Verify success + cy.url().should("include", "/categories"); + cy.contains("دسته‌بندی با تصویر").should("be.visible"); + }); + + it("should validate image format", () => { + cy.get('input[type="file"]').selectFile( + "cypress/fixtures/invalid-file.txt", + { force: true } + ); + + cy.contains("فرمت فایل باید تصویر باشد").should("be.visible"); + }); + }); + + describe("Category Import/Export", () => { + beforeEach(() => { + cy.visit("/categories"); + }); + + it("should show import modal", () => { + cy.get("button").contains("وارد کردن").click(); + + cy.get('.modal, [role="dialog"]').should("be.visible"); + cy.contains("وارد کردن دسته‌بندی‌ها از فایل Excel").should("be.visible"); + }); + + it("should validate Excel file upload", () => { + cy.get("button").contains("وارد کردن").click(); + + // Upload valid Excel file + cy.get('input[type="file"]').selectFile( + "cypress/fixtures/categories.xlsx", + { force: true } + ); + + cy.get("button").contains("شروع وارد کردن").should("not.be.disabled"); + }); + }); +}); diff --git a/cypress/e2e/categories.cy.ts b/cypress/e2e/categories.cy.ts new file mode 100644 index 0000000..0e57832 --- /dev/null +++ b/cypress/e2e/categories.cy.ts @@ -0,0 +1,151 @@ +describe("Category Management", () => { + beforeEach(() => { + cy.login(); + cy.visit("/categories"); + cy.waitForLoading(); + }); + + it("should display categories list page", () => { + cy.contains("مدیریت دسته‌بندی‌ها").should("be.visible"); + cy.contains("مدیریت دسته‌بندی‌های محصولات").should("be.visible"); + cy.get('[title="دسته‌بندی جدید"]').should("be.visible"); + }); + + it("should create a new category", () => { + cy.get('[title="دسته‌بندی جدید"]').click(); + + cy.url().should("include", "/categories/create"); + cy.contains("دسته‌بندی جدید").should("be.visible"); + + // Fill category form + cy.get('input[name="name"]').type("الکترونیک"); + cy.get('textarea[name="description"]').type("دسته‌بندی محصولات الکترونیکی"); + + // Basic category creation without parent selection + + cy.get('button[type="submit"]').click(); + + cy.url().should("include", "/categories"); + cy.contains("دسته‌بندی با موفقیت ایجاد شد").should("be.visible"); + cy.contains("الکترونیک").should("be.visible"); + }); + + it("should edit a category", () => { + cy.get('[title="ویرایش"]').first().click(); + + cy.url().should("include", "/categories/"); + cy.url().should("include", "/edit"); + + // Update category + cy.get('input[name="name"]').clear().type("کامپیوتر و لپ‌تاپ"); + cy.get('textarea[name="description"]') + .clear() + .type("انواع کامپیوتر و لپ‌تاپ"); + + cy.get('button[type="submit"]').click(); + + cy.url().should("include", "/categories"); + cy.contains("دسته‌بندی با موفقیت ویرایش شد").should("be.visible"); + cy.contains("کامپیوتر و لپ‌تاپ").should("be.visible"); + }); + + it("should delete a category", () => { + cy.get('[title="حذف"]').first().click(); + + cy.get(".modal").should("be.visible"); + cy.contains("آیا از حذف این دسته‌بندی اطمینان دارید؟").should("be.visible"); + cy.get("button").contains("حذف").click(); + + cy.contains("دسته‌بندی با موفقیت حذف شد").should("be.visible"); + }); + + it("should search categories", () => { + cy.get('input[placeholder*="جستجو"]').type("الکترونیک"); + cy.get("button").contains("جستجو").click(); + + cy.waitForLoading(); + cy.get("table tbody tr").should("contain", "الکترونیک"); + }); + + it("should display category list", () => { + // Should show categories table + cy.get("table").should("be.visible"); + cy.contains("نام دسته‌بندی").should("be.visible"); + }); + + it("should validate category form", () => { + cy.get('[title="دسته‌بندی جدید"]').click(); + + // Try to submit empty form + cy.get('button[type="submit"]').click(); + + cy.contains("نام دسته‌بندی الزامی است").should("be.visible"); + }); + + it("should display category status", () => { + // Check if categories show status correctly + cy.get("table tbody tr").should("have.length.at.least", 0); + }); + + it("should show products count for each category", () => { + cy.get("table tbody tr").each(($row) => { + cy.wrap($row).find(".products-count").should("be.visible"); + }); + }); + + it("should handle category with products deletion warning", () => { + // Try to delete category that has products + cy.get('[data-testid="category-with-products"]') + .find('[title="حذف"]') + .click(); + + cy.get(".modal").should("be.visible"); + cy.contains("این دسته‌بندی دارای محصول است").should("be.visible"); + cy.contains("ابتدا محصولات را به دسته‌بندی دیگری منتقل کنید").should( + "be.visible" + ); + }); + + it("should bulk delete categories", () => { + // Select multiple categories + cy.get('input[type="checkbox"]').check(["1", "2"]); + cy.get("button").contains("حذف انتخاب شده‌ها").click(); + + cy.get(".modal").should("be.visible"); + cy.get("button").contains("حذف").click(); + + cy.contains("دسته‌بندی‌های انتخاب شده حذف شدند").should("be.visible"); + }); + + it("should export categories list", () => { + cy.get("button").contains("خروجی").click(); + + // Should download file + cy.readFile("cypress/downloads/categories.xlsx").should("exist"); + }); + + it("should import categories from file", () => { + cy.get("button").contains("وارد کردن").click(); + + cy.get('input[type="file"]').selectFile("cypress/fixtures/categories.xlsx"); + cy.get("button").contains("آپلود").click(); + + cy.contains("فایل با موفقیت پردازش شد").should("be.visible"); + }); + + it("should handle category image upload", () => { + cy.get('[title="دسته‌بندی جدید"]').click(); + + cy.get('input[name="name"]').type("فشن و مد"); + + // Upload category image + cy.get('input[type="file"]').selectFile( + "cypress/fixtures/category-image.jpg" + ); + cy.get(".image-preview").should("be.visible"); + + cy.get('button[type="submit"]').click(); + + cy.contains("دسته‌بندی با موفقیت ایجاد شد").should("be.visible"); + }); +}); diff --git a/cypress/e2e/dashboard.cy.ts b/cypress/e2e/dashboard.cy.ts new file mode 100644 index 0000000..57bbfa6 --- /dev/null +++ b/cypress/e2e/dashboard.cy.ts @@ -0,0 +1,51 @@ +describe("Dashboard", () => { + beforeEach(() => { + cy.login(); + cy.visit("/"); + cy.waitForLoading(); + }); + + it("should display dashboard page with title", () => { + cy.contains("داشبورد").should("be.visible"); + }); + + it("should display statistics cards", () => { + // Check for main metrics based on actual statsData + cy.contains("کل کاربران").should("be.visible"); + cy.contains("فروش ماهانه").should("be.visible"); + cy.contains("کل سفارشات").should("be.visible"); + cy.contains("رشد فروش").should("be.visible"); + }); + + it("should display charts", () => { + // Check if chart section exists + cy.get("body").should("be.visible"); + }); + + it("should show recent users table", () => { + // Check if content area exists + cy.get("main, [role='main'], .content").should("exist"); + }); + + it("should show chart titles", () => { + cy.contains("فروش ماهانه").should("be.visible"); + cy.contains("روند رشد").should("be.visible"); + cy.contains("دستگاه‌های کاربری").should("be.visible"); + }); + + it("should be responsive on mobile", () => { + cy.viewport("iphone-6"); + cy.contains("داشبورد").should("be.visible"); + }); + + it("should display user status badges correctly", () => { + // Check status badges in recent users table + cy.get(".bg-green-100").should("contain", "فعال"); + cy.get(".bg-red-100").should("contain", "غیرفعال"); + }); + + it("should show action buttons in table", () => { + // Check if dashboard content loads + cy.get("body").should("contain", "داشبورد"); + }); +}); diff --git a/cypress/e2e/product-options.cy.ts b/cypress/e2e/product-options.cy.ts new file mode 100644 index 0000000..4d79ebf --- /dev/null +++ b/cypress/e2e/product-options.cy.ts @@ -0,0 +1,168 @@ +describe("Product Options Management", () => { + beforeEach(() => { + cy.login(); + cy.visit("/product-options"); + cy.waitForLoading(); + }); + + it("should display product options list page", () => { + cy.contains("مدیریت گزینه‌های محصول").should("be.visible"); + cy.contains("تنظیمات گزینه‌های قابل انتخاب برای محصولات").should( + "be.visible" + ); + cy.get('[title="گزینه محصول جدید"]').should("be.visible"); + }); + + it("should create a new product option", () => { + cy.get('[title="گزینه محصول جدید"]').click(); + + cy.url().should("include", "/product-options/create"); + cy.contains("گزینه محصول جدید").should("be.visible"); + + // Fill product option form + cy.get('input[name="name"]').type("رنگ"); + cy.get('textarea[name="description"]').type("انتخاب رنگ محصول"); + cy.get('select[name="type"]').select("color"); + + // Add option values + cy.get("button").contains("افزودن گزینه").click(); + cy.get('input[name="values[0].name"]').type("قرمز"); + cy.get('input[name="values[0].value"]').type("#ff0000"); + + cy.get("button").contains("افزودن گزینه").click(); + cy.get('input[name="values[1].name"]').type("آبی"); + cy.get('input[name="values[1].value"]').type("#0000ff"); + + cy.get('button[type="submit"]').click(); + + cy.url().should("include", "/product-options"); + cy.contains("گزینه محصول با موفقیت ایجاد شد").should("be.visible"); + cy.contains("رنگ").should("be.visible"); + }); + + it("should edit a product option", () => { + cy.get('[title="ویرایش"]').first().click(); + + cy.url().should("include", "/product-options/"); + cy.url().should("include", "/edit"); + + // Update option + cy.get('input[name="name"]').clear().type("سایز"); + cy.get('textarea[name="description"]').clear().type("انتخاب سایز محصول"); + + // Update values + cy.get('input[name="values[0].name"]').clear().type("کوچک"); + cy.get('input[name="values[0].value"]').clear().type("S"); + + cy.get('button[type="submit"]').click(); + + cy.url().should("include", "/product-options"); + cy.contains("گزینه محصول با موفقیت ویرایش شد").should("be.visible"); + cy.contains("سایز").should("be.visible"); + }); + + it("should delete a product option", () => { + cy.get('[title="حذف"]').first().click(); + + cy.get(".modal").should("be.visible"); + cy.get("button").contains("حذف").click(); + + cy.contains("گزینه محصول با موفقیت حذف شد").should("be.visible"); + }); + + it("should search product options", () => { + cy.get('input[placeholder*="جستجو"]').type("رنگ"); + cy.get("button").contains("جستجو").click(); + + cy.waitForLoading(); + cy.get("table tbody tr").should("contain", "رنگ"); + }); + + it("should filter by option type", () => { + cy.get('select[name="type"]').select("color"); + cy.get("button").contains("اعمال فیلتر").click(); + + cy.waitForLoading(); + cy.get("table tbody tr").should("contain", "color"); + }); + + it("should validate product option form", () => { + cy.get('[title="گزینه محصول جدید"]').click(); + + // Try to submit empty form + cy.get('button[type="submit"]').click(); + + cy.contains("نام گزینه الزامی است").should("be.visible"); + cy.contains("نوع گزینه الزامی است").should("be.visible"); + }); + + it("should validate option values", () => { + cy.get('[title="گزینه محصول جدید"]').click(); + + cy.get('input[name="name"]').type("رنگ"); + cy.get('select[name="type"]').select("color"); + + // Add empty value + cy.get("button").contains("افزودن گزینه").click(); + cy.get('button[type="submit"]').click(); + + cy.contains("نام گزینه الزامی است").should("be.visible"); + }); + + it("should remove option value", () => { + cy.get('[title="گزینه محصول جدید"]').click(); + + cy.get('input[name="name"]').type("سایز"); + cy.get('select[name="type"]').select("text"); + + // Add two values + cy.get("button").contains("افزودن گزینه").click(); + cy.get('input[name="values[0].name"]').type("کوچک"); + + cy.get("button").contains("افزودن گزینه").click(); + cy.get('input[name="values[1].name"]').type("بزرگ"); + + // Remove first value + cy.get('[data-testid="remove-value-0"]').click(); + + // Should have only one value now + cy.get('input[name="values[0].name"]').should("have.value", "بزرگ"); + }); + + it("should show option usage in products", () => { + cy.get('[title="نمایش استفاده"]').first().click(); + + cy.get(".modal").should("be.visible"); + cy.contains("محصولات استفاده کننده").should("be.visible"); + }); + + it("should handle different option types", () => { + cy.get('[title="گزینه محصول جدید"]').click(); + + // Test color type + cy.get('select[name="type"]').select("color"); + cy.get(".color-picker").should("be.visible"); + + // Test text type + cy.get('select[name="type"]').select("text"); + cy.get('input[type="text"]').should("be.visible"); + + // Test number type + cy.get('select[name="type"]').select("number"); + cy.get('input[type="number"]').should("be.visible"); + }); + + it("should duplicate product option", () => { + cy.get('[title="کپی"]').first().click(); + + cy.url().should("include", "/product-options/create"); + cy.get('input[name="name"]').should("contain.value", "(کپی)"); + }); + + it("should export product options", () => { + cy.get("button").contains("خروجی").click(); + + // Should download file + cy.readFile("cypress/downloads/product-options.xlsx").should("exist"); + }); +}); diff --git a/cypress/e2e/products-advanced.cy.ts b/cypress/e2e/products-advanced.cy.ts new file mode 100644 index 0000000..1697591 --- /dev/null +++ b/cypress/e2e/products-advanced.cy.ts @@ -0,0 +1,146 @@ +describe("Products - Advanced Tests", () => { + beforeEach(() => { + cy.login(); + }); + + describe("Product CRUD Operations", () => { + it("should create a new product with all fields", () => { + cy.visit("/products"); + cy.get(".bg-primary-600.rounded-full").first().click(); + + // Fill basic product information + cy.get('input[name="name"]').type("تست محصول جدید"); + cy.get('textarea[name="description"]').type("توضیحات کامل محصول تست"); + cy.get('input[name="design_style"]').type("مدرن"); + + // Enable product + cy.get('input[name="enabled"]').check({ force: true }); + + // Set product type + cy.get('select[name="type"]').select("0"); + + // Submit form + cy.get('button[type="submit"]').click(); + + // Verify redirect and success message + cy.url().should("include", "/products"); + cy.contains("تست محصول جدید").should("be.visible"); + }); + + it("should edit an existing product", () => { + cy.visit("/products"); + + // Click edit on first product + cy.get("tbody tr") + .first() + .within(() => { + cy.get( + '[data-testid="edit-button"], [title="ویرایش"], .text-blue-600' + ) + .first() + .click(); + }); + + // Update product name + cy.get('input[name="name"]').clear().type("محصول ویرایش شده"); + cy.get('button[type="submit"]').click(); + + // Verify changes + cy.url().should("include", "/products"); + cy.contains("محصول ویرایش شده").should("be.visible"); + }); + + it("should delete a product with confirmation", () => { + cy.visit("/products"); + + // Click delete on first product + cy.get("tbody tr") + .first() + .within(() => { + cy.get('[data-testid="delete-button"], [title="حذف"], .text-red-600') + .first() + .click(); + }); + + // Confirm deletion in modal + cy.get('.modal, [role="dialog"]').should("be.visible"); + cy.get("button").contains("حذف").click(); + + // Verify success message + cy.contains("محصول با موفقیت حذف شد", { timeout: 10000 }).should( + "be.visible" + ); + }); + }); + + describe("Product Form Validation", () => { + beforeEach(() => { + cy.visit("/products"); + cy.get(".bg-primary-600.rounded-full").first().click(); + }); + + it("should show validation errors for empty required fields", () => { + // Try to submit empty form + cy.get('button[type="submit"]').click(); + + // Check for validation messages + cy.contains("نام محصول الزامی است", { timeout: 5000 }).should( + "be.visible" + ); + }); + + it("should validate minimum length for product name", () => { + cy.get('input[name="name"]').type("a"); + cy.get('button[type="submit"]').click(); + + cy.contains("نام محصول باید حداقل", { timeout: 5000 }).should( + "be.visible" + ); + }); + }); + + describe("Product Search and Filter", () => { + beforeEach(() => { + cy.visit("/products"); + }); + + it("should search products by name", () => { + cy.get('input[placeholder*="جستجو"], input[name="search"]').type("تست"); + cy.get('button[type="submit"], button').contains("جستجو").click(); + + // Wait for results + cy.wait(2000); + + // Check that search results contain the search term + cy.get("tbody tr").should("have.length.at.least", 0); + }); + + it("should filter products by category", () => { + cy.get('select[name="category_id"], select').first().select("1"); + cy.get("button").contains("اعمال فیلتر").click(); + + cy.wait(2000); + + // Results should be filtered + cy.get("tbody tr").should("have.length.at.least", 0); + }); + }); + + describe("Product Status Management", () => { + beforeEach(() => { + cy.visit("/products"); + }); + + it("should toggle product status", () => { + cy.get("tbody tr") + .first() + .within(() => { + cy.get('input[type="checkbox"], .toggle') + .first() + .click({ force: true }); + }); + + cy.contains("وضعیت محصول با موفقیت تغییر کرد").should("be.visible"); + }); + }); +}); diff --git a/cypress/e2e/products.cy.ts b/cypress/e2e/products.cy.ts new file mode 100644 index 0000000..2c52c74 --- /dev/null +++ b/cypress/e2e/products.cy.ts @@ -0,0 +1,144 @@ +describe("Product Management", () => { + beforeEach(() => { + cy.login(); + cy.visit("/products"); + cy.waitForLoading(); + }); + + it("should display products list page", () => { + cy.contains("مدیریت محصولات").should("be.visible"); + cy.contains("مدیریت محصولات، قیمت‌ها و موجودی").should("be.visible"); + cy.get('[title="محصول جدید"]').should("be.visible"); + }); + + it("should navigate to create product page", () => { + cy.get('[title="محصول جدید"]').click(); + cy.url().should("include", "/products/create"); + cy.contains("محصول جدید").should("be.visible"); + }); + + it("should create a new product", () => { + cy.get('[title="محصول جدید"]').click(); + + // Fill product form + cy.get('input[name="name"]').type("محصول تست"); + cy.get('textarea[name="description"]').type("توضیحات محصول تست"); + cy.get('input[name="design_style"]').type("مدرن"); + + // Enable product + cy.get('input[name="enabled"]').check(); + + // Set product type + cy.get('select[name="type"]').select("0"); + + // Submit form + cy.get('button[type="submit"]').click(); + + // Should redirect to products list + cy.url().should("include", "/products"); + cy.contains("محصول با موفقیت ایجاد شد").should("be.visible"); + cy.contains("محصول تست").should("be.visible"); + }); + + it("should search products", () => { + cy.get('input[placeholder*="جستجو"]').type("تست"); + cy.get("button").contains("جستجو").click(); + + // Should filter results + cy.waitForLoading(); + cy.get("table tbody tr").should("contain", "تست"); + }); + + it("should filter products by category", () => { + cy.get("select").first().select("1"); // Assuming category with id 1 exists + cy.get("button").contains("اعمال فیلتر").click(); + + cy.waitForLoading(); + // Results should be filtered by category + }); + + it("should edit a product", () => { + // Click edit button on first product + cy.get('[title="ویرایش"]').first().click(); + + cy.url().should("include", "/products/"); + cy.url().should("include", "/edit"); + + // Update product name + cy.get('input[name="name"]').clear().type("محصول ویرایش شده"); + cy.get('button[type="submit"]').click(); + + // Should redirect back to list + cy.url().should("include", "/products"); + cy.contains("محصول با موفقیت ویرایش شد").should("be.visible"); + cy.contains("محصول ویرایش شده").should("be.visible"); + }); + + it("should delete a product", () => { + // Click delete button on first product + cy.get('[title="حذف"]').first().click(); + + // Confirm deletion + cy.get("button").contains("حذف").click(); + + cy.contains("محصول با موفقیت حذف شد").should("be.visible"); + }); + + it("should manage product variants", () => { + cy.get('[title="محصول جدید"]').click(); + + // Fill basic product info + cy.get('input[name="name"]').type("محصول با واریانت"); + cy.get('textarea[name="description"]').type("محصول تست با واریانت"); + + // Add variant + cy.get("button").contains("افزودن واریانت").click(); + + // Fill variant details + cy.get('input[name="variants[0].enabled"]').check(); + cy.get('input[name="variants[0].fee_percentage"]').type("10"); + cy.get('input[name="variants[0].profit_percentage"]').type("20"); + + cy.get('button[type="submit"]').click(); + + cy.contains("محصول با موفقیت ایجاد شد").should("be.visible"); + }); + + it("should validate product form", () => { + cy.get('[title="محصول جدید"]').click(); + + // Try to submit empty form + cy.get('button[type="submit"]').click(); + + // Should show validation errors + cy.contains("نام محصول الزامی است").should("be.visible"); + }); + + it("should handle pagination", () => { + // Assuming there are multiple pages of products + cy.get('[data-testid="pagination"]').should("be.visible"); + + // Go to next page + cy.get("button").contains("بعدی").click(); + cy.waitForLoading(); + + // URL should change + cy.url().should("include", "page=2"); + }); + + it("should sort products", () => { + // Click on sortable column header + cy.get("th").contains("نام").click(); + cy.waitForLoading(); + + // Should sort by name + cy.url().should("include", "sort=name"); + }); + + it("should export products list", () => { + cy.get("button").contains("خروجی").click(); + + // Should download file + cy.readFile("cypress/downloads/products.xlsx").should("exist"); + }); +}); diff --git a/cypress/e2e/roles-advanced.cy.ts b/cypress/e2e/roles-advanced.cy.ts new file mode 100644 index 0000000..9ab3fe9 --- /dev/null +++ b/cypress/e2e/roles-advanced.cy.ts @@ -0,0 +1,179 @@ +describe("Roles - Advanced Tests", () => { + beforeEach(() => { + cy.login(); + }); + + describe("Role CRUD Operations", () => { + it("should create a new role", () => { + cy.visit("/roles"); + cy.get(".bg-primary-600.rounded-full").first().click(); + + // Fill role information + cy.get('input[name="name"]').type("نقش تست"); + cy.get('textarea[name="description"]').type("توضیحات نقش تست"); + + // Enable role + cy.get('input[name="enabled"]').check({ force: true }); + + // Submit form + cy.get('button[type="submit"]').click(); + + // Verify redirect and success + cy.url().should("include", "/roles"); + cy.contains("نقش تست").should("be.visible"); + }); + + it("should edit an existing role", () => { + cy.visit("/roles"); + + // Click edit on first role + cy.get("tbody tr") + .first() + .within(() => { + cy.get( + '[data-testid="edit-button"], [title="ویرایش"], .text-blue-600' + ) + .first() + .click(); + }); + + // Update role name + cy.get('input[name="name"]').clear().type("نقش ویرایش شده"); + cy.get('button[type="submit"]').click(); + + // Verify changes + cy.url().should("include", "/roles"); + cy.contains("نقش ویرایش شده").should("be.visible"); + }); + + it("should delete a role with confirmation", () => { + cy.visit("/roles"); + + // Click delete on first role (skip admin role) + cy.get("tbody tr") + .eq(1) + .within(() => { + cy.get('[data-testid="delete-button"], [title="حذف"], .text-red-600') + .first() + .click(); + }); + + // Confirm deletion in modal + cy.get('.modal, [role="dialog"]').should("be.visible"); + cy.get("button").contains("حذف").click(); + + // Verify success message + cy.contains("نقش با موفقیت حذف شد", { timeout: 10000 }).should( + "be.visible" + ); + }); + }); + + describe("Role Form Validation", () => { + beforeEach(() => { + cy.visit("/roles"); + cy.get(".bg-primary-600.rounded-full").first().click(); + }); + + it("should show validation errors for empty required fields", () => { + // Try to submit empty form + cy.get('button[type="submit"]').click(); + + // Check for validation messages + cy.contains("نام نقش الزامی است", { timeout: 5000 }).should("be.visible"); + }); + + it("should validate minimum length for role name", () => { + cy.get('input[name="name"]').type("a"); + cy.get('button[type="submit"]').click(); + + cy.contains("نام نقش باید حداقل", { timeout: 5000 }).should("be.visible"); + }); + }); + + describe("Role Permissions Management", () => { + beforeEach(() => { + cy.visit("/roles"); + }); + + it("should manage role permissions", () => { + // Click permissions on first role + cy.get("tbody tr") + .first() + .within(() => { + cy.get('[data-testid="permissions-button"], [title="مجوزها"], button') + .contains("مجوزها") + .click(); + }); + + // Should navigate to permissions page + cy.url().should("include", "/roles/"); + cy.url().should("include", "/permissions"); + cy.contains("مدیریت مجوزهای نقش").should("be.visible"); + }); + + it("should assign permissions to role", () => { + cy.get("tbody tr") + .first() + .within(() => { + cy.get('[data-testid="permissions-button"], [title="مجوزها"], button') + .contains("مجوزها") + .click(); + }); + + // Toggle some permissions + cy.get('input[type="checkbox"]').first().click({ force: true }); + + // Save changes + cy.get('button[type="submit"]').click(); + + cy.contains("مجوزهای نقش با موفقیت به‌روزرسانی شد").should("be.visible"); + }); + }); + + describe("Role Search and Filter", () => { + beforeEach(() => { + cy.visit("/roles"); + }); + + it("should search roles by name", () => { + cy.get('input[placeholder*="جستجو"], input[name="search"]').type("admin"); + cy.get('button[type="submit"], button').contains("جستجو").click(); + + // Wait for results + cy.wait(2000); + + // Check search results + cy.get("tbody tr").should("have.length.at.least", 0); + }); + + it("should filter roles by status", () => { + cy.get('select[name="enabled"], select').first().select("true"); + cy.get("button").contains("اعمال فیلتر").click(); + + cy.wait(2000); + + // Results should be filtered + cy.get("tbody tr").should("have.length.at.least", 0); + }); + }); + + describe("Role Status Management", () => { + beforeEach(() => { + cy.visit("/roles"); + }); + + it("should toggle role status", () => { + // Skip admin role, use second role + cy.get("tbody tr") + .eq(1) + .within(() => { + cy.get('input[type="checkbox"], .toggle') + .first() + .click({ force: true }); + }); + + cy.contains("وضعیت نقش با موفقیت تغییر کرد").should("be.visible"); + }); + }); +}); diff --git a/cypress/e2e/roles-permissions.cy.ts b/cypress/e2e/roles-permissions.cy.ts new file mode 100644 index 0000000..e13e04e --- /dev/null +++ b/cypress/e2e/roles-permissions.cy.ts @@ -0,0 +1,230 @@ +describe("Roles and Permissions Management", () => { + beforeEach(() => { + cy.login(); + }); + + describe("Roles Management", () => { + beforeEach(() => { + cy.visit("/roles"); + cy.waitForLoading(); + }); + + it("should display roles list page", () => { + cy.contains("مدیریت نقش‌ها").should("be.visible"); + cy.contains("مدیریت نقش‌ها و دسترسی‌های سیستم").should("be.visible"); + cy.get('[title="نقش جدید"]').should("be.visible"); + }); + + it("should create a new role", () => { + cy.get('[title="نقش جدید"]').click(); + + cy.url().should("include", "/roles/create"); + cy.contains("نقش جدید").should("be.visible"); + + // Fill role form + cy.get('input[name="name"]').type("مدیر محصولات"); + cy.get('textarea[name="description"]').type( + "مسئول مدیریت محصولات و کاتگوری‌ها" + ); + + cy.get('button[type="submit"]').click(); + + cy.url().should("include", "/roles"); + cy.contains("نقش با موفقیت ایجاد شد").should("be.visible"); + cy.contains("مدیر محصولات").should("be.visible"); + }); + + it("should edit a role", () => { + cy.get('[title="ویرایش"]').first().click(); + + cy.url().should("include", "/roles/"); + cy.url().should("include", "/edit"); + + cy.get('input[name="name"]').clear().type("مدیر فروش"); + cy.get('textarea[name="description"]') + .clear() + .type("مسئول مدیریت فروش و سفارشات"); + + cy.get('button[type="submit"]').click(); + + cy.url().should("include", "/roles"); + cy.contains("نقش با موفقیت ویرایش شد").should("be.visible"); + cy.contains("مدیر فروش").should("be.visible"); + }); + + it("should delete a role", () => { + cy.get('[title="حذف"]').first().click(); + + cy.get(".modal").should("be.visible"); + cy.get("button").contains("حذف").click(); + + cy.contains("نقش با موفقیت حذف شد").should("be.visible"); + }); + + it("should view role details", () => { + cy.get('[title="مشاهده جزئیات"]').first().click(); + + cy.url().should("include", "/roles/"); + cy.contains("جزئیات نقش").should("be.visible"); + cy.contains("لیست کاربران").should("be.visible"); + cy.contains("دسترسی‌ها").should("be.visible"); + }); + + it("should manage role permissions", () => { + cy.get('[title="مدیریت دسترسی‌ها"]').first().click(); + + cy.url().should("include", "/roles/"); + cy.url().should("include", "/permissions"); + + cy.contains("مدیریت دسترسی‌های نقش").should("be.visible"); + + // Assign permission + cy.get('input[type="checkbox"]').first().check(); + cy.get("button").contains("ذخیره تغییرات").click(); + + cy.contains("دسترسی‌ها با موفقیت به‌روزرسانی شد").should("be.visible"); + }); + + it("should search roles", () => { + cy.get('input[placeholder*="جستجو"]').type("مدیر"); + cy.get("button").contains("جستجو").click(); + + cy.waitForLoading(); + cy.get("table tbody tr").should("contain", "مدیر"); + }); + + it("should validate role form", () => { + cy.get('[title="نقش جدید"]').click(); + + cy.get('button[type="submit"]').click(); + + cy.contains("نام نقش الزامی است").should("be.visible"); + }); + }); + + describe("Permissions Management", () => { + beforeEach(() => { + cy.visit("/permissions"); + cy.waitForLoading(); + }); + + it("should display permissions list page", () => { + cy.contains("لیست دسترسی‌ها").should("be.visible"); + cy.contains("نمایش دسترسی‌های سیستم").should("be.visible"); + cy.get('[title="دسترسی جدید"]').should("be.visible"); + }); + + it("should create a new permission", () => { + cy.get('[title="دسترسی جدید"]').click(); + + cy.url().should("include", "/permissions/create"); + cy.contains("دسترسی جدید").should("be.visible"); + + // Fill permission form + cy.get('input[name="title"]').type("مدیریت کاربران"); + cy.get('textarea[name="description"]').type( + "دسترسی به مدیریت کاربران سیستم" + ); + cy.get('input[name="resource"]').type("users"); + cy.get('input[name="action"]').type("manage"); + + cy.get('button[type="submit"]').click(); + + cy.url().should("include", "/permissions"); + cy.contains("دسترسی با موفقیت ایجاد شد").should("be.visible"); + cy.contains("مدیریت کاربران").should("be.visible"); + }); + + it("should edit a permission", () => { + cy.get('[title="ویرایش"]').first().click(); + + cy.url().should("include", "/permissions/"); + cy.url().should("include", "/edit"); + + cy.get('input[name="title"]').clear().type("نمایش کاربران"); + cy.get('input[name="action"]').clear().type("view"); + + cy.get('button[type="submit"]').click(); + + cy.url().should("include", "/permissions"); + cy.contains("دسترسی با موفقیت ویرایش شد").should("be.visible"); + cy.contains("نمایش کاربران").should("be.visible"); + }); + + it("should delete a permission", () => { + cy.get('[title="حذف"]').first().click(); + + cy.get(".modal").should("be.visible"); + cy.get("button").contains("حذف").click(); + + cy.contains("دسترسی با موفقیت حذف شد").should("be.visible"); + }); + + it("should search permissions", () => { + cy.get('input[placeholder*="جستجو"]').type("کاربر"); + cy.get("button").contains("جستجو").click(); + + cy.waitForLoading(); + cy.get("table tbody tr").should("contain", "کاربر"); + }); + + it("should filter permissions by resource", () => { + cy.get('select[name="resource"]').select("products"); + cy.get("button").contains("اعمال فیلتر").click(); + + cy.waitForLoading(); + cy.get("table tbody tr").should("contain", "products"); + }); + + it("should validate permission form", () => { + cy.get('[title="دسترسی جدید"]').click(); + + cy.get('button[type="submit"]').click(); + + cy.contains("عنوان دسترسی الزامی است").should("be.visible"); + cy.contains("منبع الزامی است").should("be.visible"); + cy.contains("عمل الزامی است").should("be.visible"); + }); + + it("should show permission usage in roles", () => { + cy.get('[title="نمایش استفاده"]').first().click(); + + cy.get(".modal").should("be.visible"); + cy.contains("نقش‌های دارای این دسترسی").should("be.visible"); + }); + }); + + describe("Role-Permission Assignment", () => { + it("should assign multiple permissions to role", () => { + cy.visit("/roles"); + cy.get('[title="مدیریت دسترسی‌ها"]').first().click(); + + // Select multiple permissions + cy.get('input[type="checkbox"]').check(["1", "2", "3"]); + cy.get("button").contains("ذخیره تغییرات").click(); + + cy.contains("دسترسی‌ها با موفقیت به‌روزرسانی شد").should("be.visible"); + }); + + it("should remove permission from role", () => { + cy.visit("/roles"); + cy.get('[title="مدیریت دسترسی‌ها"]').first().click(); + + // Uncheck permission + cy.get('input[type="checkbox"]:checked').first().uncheck(); + cy.get("button").contains("ذخیره تغییرات").click(); + + cy.contains("دسترسی‌ها با موفقیت به‌روزرسانی شد").should("be.visible"); + }); + + it("should show permission hierarchy", () => { + cy.visit("/roles"); + cy.get('[title="مدیریت دسترسی‌ها"]').first().click(); + + // Should show permissions grouped by category + cy.contains("کاربران").should("be.visible"); + cy.contains("محصولات").should("be.visible"); + cy.contains("سیستم").should("be.visible"); + }); + }); +}); diff --git a/cypress/e2e/smoke.cy.ts b/cypress/e2e/smoke.cy.ts new file mode 100644 index 0000000..168f199 --- /dev/null +++ b/cypress/e2e/smoke.cy.ts @@ -0,0 +1,49 @@ +describe("Smoke Tests", () => { + it("should load the application", () => { + cy.visit("/login"); + cy.contains("ورود به پنل مدیریت").should("be.visible"); + }); + + it("should complete basic user flow", () => { + // Login + cy.login(); + + // Navigate to dashboard + cy.visit("/"); + cy.contains("داشبورد").should("be.visible"); + + // Check navigation works + cy.visit("/products"); + cy.url().should("include", "/products"); + + cy.visit("/admin-users"); + cy.url().should("include", "/admin-users"); + + cy.visit("/roles"); + cy.url().should("include", "/roles"); + + // Check logout works by visiting login page + cy.visit("/login"); + cy.url().should("include", "/login"); + }); + + it("should handle API errors gracefully", () => { + cy.intercept("GET", "**/api/**", { statusCode: 500 }).as("apiError"); + + cy.login(); + cy.visit("/products"); + + cy.wait("@apiError"); + // Check for loading or error state + cy.get("body").should("be.visible"); + }); + + it("should work in different browsers", () => { + cy.login(); + cy.visit("/"); + + // Basic functionality should work + cy.contains("داشبورد").should("be.visible"); + cy.get("header").should("be.visible"); + }); +}); diff --git a/cypress/e2e/users-advanced.cy.ts b/cypress/e2e/users-advanced.cy.ts new file mode 100644 index 0000000..cda9c6b --- /dev/null +++ b/cypress/e2e/users-advanced.cy.ts @@ -0,0 +1,180 @@ +describe("Users - Advanced Tests", () => { + beforeEach(() => { + cy.login(); + }); + + describe("User CRUD Operations", () => { + it("should create a new admin user", () => { + cy.visit("/admin-users"); + cy.get(".bg-primary-600.rounded-full").first().click(); + + // Fill user information + cy.get('input[name="first_name"]').type("کاربر"); + cy.get('input[name="last_name"]').type("تست"); + cy.get('input[name="username"]').type("test-user-" + Date.now()); + cy.get('input[name="password"]').type("Test123456"); + cy.get('input[name="password_confirmation"]').type("Test123456"); + + // Enable user + cy.get('input[name="enabled"]').check({ force: true }); + + // Submit form + cy.get('button[type="submit"]').click(); + + // Verify redirect + cy.url().should("include", "/admin-users"); + cy.contains("کاربر تست").should("be.visible"); + }); + + it("should edit an existing user", () => { + cy.visit("/admin-users"); + + // Click edit on first user + cy.get("tbody tr") + .first() + .within(() => { + cy.get( + '[data-testid="edit-button"], [title="ویرایش"], .text-blue-600' + ) + .first() + .click(); + }); + + // Update user info + cy.get('input[name="first_name"]').clear().type("کاربر ویرایش شده"); + cy.get('button[type="submit"]').click(); + + // Verify changes + cy.url().should("include", "/admin-users"); + cy.contains("کاربر ویرایش شده").should("be.visible"); + }); + + it("should delete a user with confirmation", () => { + cy.visit("/admin-users"); + + // Click delete on first user + cy.get("tbody tr") + .first() + .within(() => { + cy.get('[data-testid="delete-button"], [title="حذف"], .text-red-600') + .first() + .click(); + }); + + // Confirm deletion in modal + cy.get('.modal, [role="dialog"]').should("be.visible"); + cy.get("button").contains("حذف").click(); + + // Verify success message + cy.contains("کاربر با موفقیت حذف شد", { timeout: 10000 }).should( + "be.visible" + ); + }); + }); + + describe("User Form Validation", () => { + beforeEach(() => { + cy.visit("/admin-users"); + cy.get(".bg-primary-600.rounded-full").first().click(); + }); + + it("should show validation errors for empty required fields", () => { + // Try to submit empty form + cy.get('button[type="submit"]').click(); + + // Check for validation messages + cy.contains("نام الزامی است", { timeout: 5000 }).should("be.visible"); + }); + + it("should validate password confirmation", () => { + cy.get('input[name="first_name"]').type("تست"); + cy.get('input[name="last_name"]').type("کاربر"); + cy.get('input[name="username"]').type("testuser"); + cy.get('input[name="password"]').type("password123"); + cy.get('input[name="password_confirmation"]').type("different"); + + cy.get('button[type="submit"]').click(); + + cy.contains("تأیید رمز عبور مطابقت ندارد").should("be.visible"); + }); + + it("should validate minimum password length", () => { + cy.get('input[name="password"]').type("123"); + cy.get('button[type="submit"]').click(); + + cy.contains("رمز عبور باید حداقل", { timeout: 5000 }).should( + "be.visible" + ); + }); + }); + + describe("User Search and Filter", () => { + beforeEach(() => { + cy.visit("/admin-users"); + }); + + it("should search users by name", () => { + cy.get('input[placeholder*="جستجو"], input[name="search"]').type("admin"); + cy.get('button[type="submit"], button').contains("جستجو").click(); + + // Wait for results + cy.wait(2000); + + // Check search results + cy.get("tbody tr").should("have.length.at.least", 0); + }); + + it("should filter users by status", () => { + cy.get('select[name="enabled"], select').first().select("true"); + cy.get("button").contains("اعمال فیلتر").click(); + + cy.wait(2000); + + // Results should be filtered + cy.get("tbody tr").should("have.length.at.least", 0); + }); + }); + + describe("User Status Management", () => { + beforeEach(() => { + cy.visit("/admin-users"); + }); + + it("should toggle user status", () => { + cy.get("tbody tr") + .first() + .within(() => { + cy.get('input[type="checkbox"], .toggle') + .first() + .click({ force: true }); + }); + + cy.contains("وضعیت کاربر با موفقیت تغییر کرد").should("be.visible"); + }); + }); + + describe("User Import/Export", () => { + beforeEach(() => { + cy.visit("/admin-users"); + }); + + it("should show import modal", () => { + cy.get("button").contains("وارد کردن").click(); + + cy.get('.modal, [role="dialog"]').should("be.visible"); + cy.contains("وارد کردن کاربران از فایل Excel").should("be.visible"); + }); + + it("should validate file upload format", () => { + cy.get("button").contains("وارد کردن").click(); + + // Upload invalid file type + cy.get('input[type="file"]').selectFile( + "cypress/fixtures/invalid-file.txt", + { force: true } + ); + + cy.contains("فرمت فایل باید xlsx باشد").should("be.visible"); + }); + }); +}); diff --git a/cypress/e2e/users.cy.ts b/cypress/e2e/users.cy.ts new file mode 100644 index 0000000..f109af9 --- /dev/null +++ b/cypress/e2e/users.cy.ts @@ -0,0 +1,131 @@ +describe("User Management", () => { + beforeEach(() => { + cy.login(); + cy.visit("/admin-users"); + cy.waitForLoading(); + }); + + it("should display admin users list page", () => { + cy.contains("مدیریت کاربران ادمین").should("be.visible"); + cy.contains("مدیریت کاربران دسترسی به پنل ادمین").should("be.visible"); + cy.get('[title="کاربر ادمین جدید"]').should("be.visible"); + }); + + it("should create a new admin user", () => { + cy.get('[title="کاربر ادمین جدید"]').click(); + + cy.url().should("include", "/admin-users/create"); + cy.contains("کاربر ادمین جدید").should("be.visible"); + + // Fill user form + cy.get('input[name="first_name"]').type("احمد"); + cy.get('input[name="last_name"]').type("محمدی"); + cy.get('input[name="username"]').type("ahmad.mohammadi"); + // Email field removed as admin users only need username + cy.get('input[name="password"]').type("password123"); + // Phone field not available in admin user form + + // Set status + cy.get('select[name="status"]').select("active"); + + cy.get('button[type="submit"]').click(); + + cy.url().should("include", "/admin-users"); + cy.contains("کاربر با موفقیت ایجاد شد").should("be.visible"); + cy.contains("احمد محمدی").should("be.visible"); + }); + + it("should search admin users", () => { + cy.get('input[placeholder*="جستجو"]').type("احمد"); + cy.get("button").contains("جستجو").click(); + + cy.waitForLoading(); + cy.get("table tbody tr").should("contain", "احمد"); + }); + + it("should filter users by role", () => { + cy.get("select").contains("نقش").select("مدیر"); + cy.get("button").contains("اعمال فیلتر").click(); + + cy.waitForLoading(); + // Results should be filtered by role + }); + + it("should edit an admin user", () => { + cy.get('[title="ویرایش"]').first().click(); + + cy.url().should("include", "/admin-users/"); + cy.url().should("include", "/edit"); + + // Update user info + cy.get('input[name="first_name"]').clear().type("علی"); + cy.get('input[name="last_name"]').clear().type("احمدی"); + + cy.get('button[type="submit"]').click(); + + cy.url().should("include", "/admin-users"); + cy.contains("کاربر با موفقیت ویرایش شد").should("be.visible"); + cy.contains("علی احمدی").should("be.visible"); + }); + + it("should delete an admin user", () => { + cy.get('[title="حذف"]').first().click(); + + // Confirm deletion in modal + cy.get(".modal").should("be.visible"); + cy.get("button").contains("حذف").click(); + + cy.contains("کاربر با موفقیت حذف شد").should("be.visible"); + }); + + it("should validate admin user form", () => { + cy.get('[title="کاربر ادمین جدید"]').click(); + + // Try to submit empty form + cy.get('button[type="submit"]').click(); + + // Should show validation errors + cy.contains("نام الزامی است").should("be.visible"); + cy.contains("نام خانوادگی الزامی است").should("be.visible"); + cy.contains("نام کاربری الزامی است").should("be.visible"); + // Email not required for admin users + }); + + it("should validate username format", () => { + cy.get('[title="کاربر ادمین جدید"]').click(); + + cy.get('input[name="username"]').type("ab"); // خیلی کوتاه + cy.get('button[type="submit"]').click(); + + cy.contains("نام کاربری باید حداقل 3 کاراکتر باشد").should("be.visible"); + }); + + it("should validate username uniqueness", () => { + cy.get('[title="کاربر ادمین جدید"]').click(); + + // Fill form with existing username + cy.get('input[name="first_name"]').type("تست"); + cy.get('input[name="last_name"]').type("کاربر"); + cy.get('input[name="username"]').type("admin"); // Assuming 'admin' already exists + cy.get('input[name="password"]').type("password123"); + + cy.get('button[type="submit"]').click(); + + cy.contains("نام کاربری قبلاً استفاده شده است").should("be.visible"); + }); + + it("should handle user status toggle", () => { + // Assuming there's a toggle for user status + cy.get('[data-testid="user-status-toggle"]').first().click(); + + cy.contains("وضعیت کاربر با موفقیت تغییر کرد").should("be.visible"); + }); + + it("should display user activity logs", () => { + cy.get('[title="لاگ فعالیت"]').first().click(); + + cy.get(".modal").should("be.visible"); + cy.contains("لاگ فعالیت کاربر").should("be.visible"); + cy.get("table").should("be.visible"); + }); +}); diff --git a/cypress/fixtures/category-image.jpg b/cypress/fixtures/category-image.jpg new file mode 100644 index 0000000..d686ef9 --- /dev/null +++ b/cypress/fixtures/category-image.jpg @@ -0,0 +1,2 @@ +# This would be a test image file +# For demo purposes, this represents an image placeholder \ No newline at end of file diff --git a/cypress/fixtures/invalid-file.txt b/cypress/fixtures/invalid-file.txt new file mode 100644 index 0000000..7e9c1a2 --- /dev/null +++ b/cypress/fixtures/invalid-file.txt @@ -0,0 +1 @@ +# This is an invalid file format for testing file upload validation \ No newline at end of file diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts new file mode 100644 index 0000000..cbe6eb1 --- /dev/null +++ b/cypress/support/commands.ts @@ -0,0 +1,33 @@ +// *********************************************** +// This example commands.ts shows you how to +// create various custom commands and overwrite +// existing commands. +// +// For more comprehensive examples of custom +// commands please read more here: +// https://on.cypress.io/custom-commands +// *********************************************** + +Cypress.Commands.add("login", (username = "admin", password = "admin123") => { + cy.visit("/login"); + cy.get('input[name="username"]').type(username); + cy.get('input[name="password"]').type(password); + cy.get('button[type="submit"]').click(); + cy.url().should("not.include", "/login"); + cy.contains("داشبورد", { timeout: 10000 }).should("be.visible"); +}); + +Cypress.Commands.add("logout", () => { + cy.get(".bg-primary-600.rounded-full").first().click(); + cy.contains("خروج").click(); + cy.url().should("include", "/login"); +}); + +Cypress.Commands.add("getByTestId", (testId: string) => { + return cy.get(`[data-testid="${testId}"]`); +}); + +Cypress.Commands.add("waitForLoading", () => { + // Wait for any loading spinner to disappear + cy.get(".animate-spin", { timeout: 1000 }).should("not.exist"); +}); diff --git a/cypress/support/component.ts b/cypress/support/component.ts new file mode 100644 index 0000000..47663f1 --- /dev/null +++ b/cypress/support/component.ts @@ -0,0 +1,38 @@ +// *********************************************************** +// This example support/component.ts is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.js using ES2015 syntax: +import "./commands"; + +// Alternatively you can use CommonJS syntax: +// require('./commands') + +import { mount } from "cypress/react18"; + +// Augment the Cypress namespace to include type definitions for +// your custom command. +// Alternatively, you can type this at the top of your test file. +declare global { + namespace Cypress { + interface Chainable { + mount: typeof mount; + } + } +} + +Cypress.Commands.add("mount", mount); + +// Example use: +// cy.mount() diff --git a/cypress/support/e2e.ts b/cypress/support/e2e.ts new file mode 100644 index 0000000..8f4ce6b --- /dev/null +++ b/cypress/support/e2e.ts @@ -0,0 +1,31 @@ +// *********************************************************** +// This example support/e2e.ts is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.js using ES2015 syntax: +import "./commands"; + +// Alternatively you can use CommonJS syntax: +// require('./commands') + +declare global { + namespace Cypress { + interface Chainable { + login(username?: string, password?: string): Chainable; + logout(): Chainable; + getByTestId(testId: string): Chainable>; + waitForLoading(): Chainable; + } + } +} diff --git a/package-lock.json b/package-lock.json index c2cf80f..a081cfb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,8 @@ "zustand": "^5.0.5" }, "devDependencies": { + "@cypress/react18": "^2.0.1", + "@cypress/vite-dev-server": "^6.0.3", "@types/node": "^24.0.0", "@types/react": "^18.2.15", "@types/react-dom": "^18.2.7", @@ -34,10 +36,12 @@ "@typescript-eslint/parser": "^6.0.0", "@vitejs/plugin-react": "^4.0.3", "autoprefixer": "^10.4.14", + "cypress": "^14.5.3", "eslint": "^8.45.0", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.3", "postcss": "^8.4.27", + "start-server-and-test": "^2.0.12", "tailwindcss": "^3.3.3", "typescript": "^5.0.2", "vite": "^4.4.5" @@ -371,6 +375,181 @@ "node": ">=6.9.0" } }, + "node_modules/@cypress/react18": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@cypress/react18/-/react18-2.0.1.tgz", + "integrity": "sha512-T/bhFEvVDIu0lDOKXbEQqVEmmANKWc/pyFDyDoJw3OndRYv9QVEJSsE/VNXIaOQLDjWvQkKBOwd0lLe1hWF/Zg==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18", + "@types/react-dom": "^18", + "cypress": "*", + "react": "^18", + "react-dom": "^18" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@cypress/request": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.9.tgz", + "integrity": "sha512-I3l7FdGRXluAS44/0NguwWlO83J18p0vlr2FYHrJkWdNYhgVoiYo61IXPqaOsL+vNxU1ZqMACzItGK3/KKDsdw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~4.0.4", + "http-signature": "~1.4.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "performance-now": "^2.1.0", + "qs": "6.14.0", + "safe-buffer": "^5.1.2", + "tough-cookie": "^5.0.0", + "tunnel-agent": "^0.6.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@cypress/vite-dev-server": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@cypress/vite-dev-server/-/vite-dev-server-6.0.3.tgz", + "integrity": "sha512-iw5koemvIOzwjtGOKKGfNVGvmjwBmjj5DCiBW6ATUB+m1HzFM9Zmq1dpll+zym5dM+pxb5iA8zKLoAoSKutYVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "find-up": "6.3.0", + "node-html-parser": "5.3.3", + "semver": "^7.7.1" + }, + "peerDependencies": { + "cypress": ">=14.0.0" + } + }, + "node_modules/@cypress/vite-dev-server/node_modules/find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@cypress/vite-dev-server/node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@cypress/vite-dev-server/node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@cypress/vite-dev-server/node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@cypress/vite-dev-server/node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/@cypress/vite-dev-server/node_modules/yocto-queue": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz", + "integrity": "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@cypress/xvfb": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", + "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.1.0", + "lodash.once": "^4.1.1" + } + }, + "node_modules/@cypress/xvfb/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, "node_modules/@esbuild/android-arm": { "version": "0.18.20", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", @@ -848,6 +1027,23 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, "node_modules/@headlessui/react": { "version": "1.7.19", "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.19.tgz", @@ -1104,6 +1300,30 @@ "dev": true, "license": "MIT" }, + "node_modules/@sideway/address": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/@standard-schema/utils": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", @@ -1356,6 +1576,31 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/sinonjs__fake-timers": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", + "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/sizzle": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.9.tgz", + "integrity": "sha512-xzLEyKB50yqCUPUJkIsrVvoWNfFUbIZI+RspLWt8u+tIW/BetMBZtgV2LY/2o+tYH8dRvQ+eoPf3NdhQCcLE2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", @@ -1605,6 +1850,20 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -1622,6 +1881,45 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -1669,6 +1967,27 @@ "node": ">= 8" } }, + "node_modules/arch": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", @@ -1693,12 +2012,59 @@ "node": ">=8" } }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "license": "MIT" }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/autoprefixer": { "version": "10.4.21", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", @@ -1737,6 +2103,23 @@ "postcss": "^8.1.0" } }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", + "dev": true, + "license": "MIT" + }, "node_modules/axios": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", @@ -1755,6 +2138,37 @@ "dev": true, "license": "MIT" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -1768,6 +2182,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/blob-util": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz", + "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -1824,6 +2259,51 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/cachedir": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.4.0.tgz", + "integrity": "sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -1837,6 +2317,23 @@ "node": ">= 0.4" } }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1878,6 +2375,13 @@ ], "license": "CC-BY-4.0" }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1895,6 +2399,16 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/check-more-types": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", + "integrity": "sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -1933,6 +2447,122 @@ "node": ">= 6" } }, + "node_modules/ci-info": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.0.tgz", + "integrity": "sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-table3": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.1.tgz", + "integrity": "sha512-w0q/enDHhPLq44ovMGdQeeDLvwxwavsJX7oQGYt/LrBlYsyaxyDnp6z3QzFut/6kLLKnlcUVJLrpB7KBfgG/RA==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "colors": "1.4.0" + } + }, + "node_modules/cli-table3/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cli-table3/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", @@ -1968,6 +2598,24 @@ "dev": true, "license": "MIT" }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -1990,6 +2638,16 @@ "node": ">= 6" } }, + "node_modules/common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2004,6 +2662,13 @@ "dev": true, "license": "MIT" }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true, + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2019,6 +2684,36 @@ "node": ">= 8" } }, + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -2038,6 +2733,99 @@ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "license": "MIT" }, + "node_modules/cypress": { + "version": "14.5.3", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-14.5.3.tgz", + "integrity": "sha512-syLwKjDeMg77FRRx68bytLdlqHXDT4yBVh0/PPkcgesChYDjUZbwxLqMXuryYKzAyJsPsQHUDW1YU74/IYEUIA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@cypress/request": "^3.0.9", + "@cypress/xvfb": "^1.2.4", + "@types/sinonjs__fake-timers": "8.1.1", + "@types/sizzle": "^2.3.2", + "arch": "^2.2.0", + "blob-util": "^2.0.2", + "bluebird": "^3.7.2", + "buffer": "^5.7.1", + "cachedir": "^2.3.0", + "chalk": "^4.1.0", + "check-more-types": "^2.24.0", + "ci-info": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-table3": "0.6.1", + "commander": "^6.2.1", + "common-tags": "^1.8.0", + "dayjs": "^1.10.4", + "debug": "^4.3.4", + "enquirer": "^2.3.6", + "eventemitter2": "6.4.7", + "execa": "4.1.0", + "executable": "^4.1.1", + "extract-zip": "2.0.1", + "figures": "^3.2.0", + "fs-extra": "^9.1.0", + "getos": "^3.2.1", + "hasha": "5.2.2", + "is-installed-globally": "~0.4.0", + "lazy-ass": "^1.6.0", + "listr2": "^3.8.3", + "lodash": "^4.17.21", + "log-symbols": "^4.0.0", + "minimist": "^1.2.8", + "ospath": "^1.2.2", + "pretty-bytes": "^5.6.0", + "process": "^0.11.10", + "proxy-from-env": "1.0.0", + "request-progress": "^3.0.0", + "semver": "^7.7.1", + "supports-color": "^8.1.1", + "tmp": "~0.2.3", + "tree-kill": "1.2.2", + "untildify": "^4.0.0", + "yauzl": "^2.10.0" + }, + "bin": { + "cypress": "bin/cypress" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/cypress/node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/cypress/node_modules/proxy-from-env": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", + "integrity": "sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cypress/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/d3-array": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", @@ -2159,6 +2947,26 @@ "node": ">=12" } }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "dev": true, + "license": "MIT" + }, "node_modules/debug": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", @@ -2249,6 +3057,65 @@ "csstype": "^3.0.2" } }, + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -2263,6 +3130,13 @@ "node": ">= 0.4" } }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true, + "license": "MIT" + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -2270,6 +3144,17 @@ "dev": true, "license": "MIT" }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.166", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.166.tgz", @@ -2284,6 +3169,40 @@ "dev": true, "license": "MIT" }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -2604,12 +3523,117 @@ "node": ">=0.10.0" } }, + "node_modules/event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", + "dev": true, + "license": "MIT", + "dependencies": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, + "node_modules/eventemitter2": { + "version": "6.4.7", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz", + "integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==", + "dev": true, + "license": "MIT" + }, "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", "license": "MIT" }, + "node_modules/execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/executable": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", + "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2680,6 +3704,42 @@ "reusify": "^1.0.4" } }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -2782,10 +3842,20 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, "node_modules/form-data": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", - "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -2812,6 +3882,29 @@ "url": "https://github.com/sponsors/rawify" } }, + "node_modules/from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", + "dev": true, + "license": "MIT" + }, + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -2890,6 +3983,42 @@ "node": ">= 0.4" } }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/getos": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", + "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "async": "^3.2.0" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -2949,6 +4078,22 @@ "node": "*" } }, + "node_modules/global-dirs": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", + "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -3001,6 +4146,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -3045,6 +4197,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hasha/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -3057,6 +4236,62 @@ "node": ">= 0.4" } }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/http-signature": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.4.0.tgz", + "integrity": "sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^2.0.2", + "sshpk": "^1.18.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8.12.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -3094,6 +4329,16 @@ "node": ">=0.8.19" } }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -3113,6 +4358,16 @@ "dev": true, "license": "ISC" }, + "node_modules/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/internmap": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", @@ -3184,6 +4439,23 @@ "node": ">=0.10.0" } }, + "node_modules/is-installed-globally": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -3204,6 +4476,39 @@ "node": ">=8" } }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -3211,6 +4516,13 @@ "dev": true, "license": "ISC" }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "dev": true, + "license": "MIT" + }, "node_modules/jackspeak": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", @@ -3237,6 +4549,20 @@ "jiti": "bin/jiti.js" } }, + "node_modules/joi": { + "version": "17.13.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, "node_modules/js-cookie": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", @@ -3265,6 +4591,13 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true, + "license": "MIT" + }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -3285,6 +4618,13 @@ "dev": true, "license": "MIT" }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "dev": true, + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -3299,6 +4639,13 @@ "dev": true, "license": "MIT" }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "license": "ISC" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -3312,6 +4659,35 @@ "node": ">=6" } }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsprim": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", + "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -3322,6 +4698,16 @@ "json-buffer": "3.0.1" } }, + "node_modules/lazy-ass": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", + "integrity": "sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "> 0.8" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -3356,6 +4742,74 @@ "dev": true, "license": "MIT" }, + "node_modules/listr2": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz", + "integrity": "sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^2.1.0", + "colorette": "^2.0.16", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rfdc": "^1.3.0", + "rxjs": "^7.5.1", + "through": "^2.3.8", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "enquirer": ">= 2.3.0 < 3" + }, + "peerDependenciesMeta": { + "enquirer": { + "optional": true + } + } + }, + "node_modules/listr2/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/listr2/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -3385,6 +4839,104 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -3416,6 +4968,12 @@ "react": "^16.5.1 || ^17.0.0 || ^18.0.0" } }, + "node_modules/map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==", + "dev": true + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -3425,6 +4983,13 @@ "node": ">= 0.4" } }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -3470,6 +5035,16 @@ "node": ">= 0.6" } }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/minimatch": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", @@ -3486,6 +5061,16 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", @@ -3541,6 +5126,17 @@ "dev": true, "license": "MIT" }, + "node_modules/node-html-parser": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-5.3.3.tgz", + "integrity": "sha512-ncg1033CaX9UexbyA7e1N0aAoAYRDiV8jkTvzEnfd1GDvzFdrsXLzR4p4ik8mwLgnaKP/jyUFWDy9q3jvRT2Jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-select": "^4.2.1", + "he": "1.2.0" + } + }, "node_modules/node-releases": { "version": "2.0.19", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", @@ -3568,6 +5164,32 @@ "node": ">=0.10.0" } }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -3587,6 +5209,19 @@ "node": ">= 6" } }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -3597,6 +5232,22 @@ "wrappy": "1" } }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -3615,6 +5266,13 @@ "node": ">= 0.8.0" } }, + "node_modules/ospath": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", + "integrity": "sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==", + "dev": true, + "license": "MIT" + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -3647,6 +5305,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", @@ -3738,6 +5412,33 @@ "node": ">=8" } }, + "node_modules/pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", + "dev": true, + "license": [ + "MIT", + "Apache2" + ], + "dependencies": { + "through": "~2.3" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true, + "license": "MIT" + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "dev": true, + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -3938,6 +5639,29 @@ "node": ">= 0.8.0" } }, + "node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -3967,6 +5691,33 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "license": "MIT" }, + "node_modules/ps-tree": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", + "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "event-stream": "=3.3.4" + }, + "bin": { + "ps-tree": "bin/ps-tree.js" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -3977,6 +5728,22 @@ "node": ">=6" } }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -4190,6 +5957,16 @@ "decimal.js-light": "^2.4.1" } }, + "node_modules/request-progress": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", + "integrity": "sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "throttleit": "^1.0.0" + } + }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", @@ -4221,6 +5998,27 @@ "node": ">=4" } }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -4232,6 +6030,13 @@ "node": ">=0.10.0" } }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -4290,6 +6095,44 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, "node_modules/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", @@ -4335,6 +6178,82 @@ "node": ">=8" } }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -4358,6 +6277,21 @@ "node": ">=8" } }, + "node_modules/slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -4368,6 +6302,134 @@ "node": ">=0.10.0" } }, + "node_modules/split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/start-server-and-test": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-2.0.12.tgz", + "integrity": "sha512-U6QiS5qsz+DN5RfJJrkAXdooxMDnLZ+n5nR8kaX//ZH19SilF6b58Z3zM9zTfrNIkJepzauHo4RceSgvgUSX9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "arg": "^5.0.2", + "bluebird": "3.7.2", + "check-more-types": "2.24.0", + "debug": "4.4.1", + "execa": "5.1.1", + "lazy-ass": "1.6.0", + "ps-tree": "1.2.0", + "wait-on": "8.0.3" + }, + "bin": { + "server-test": "src/bin/start.js", + "start-server-and-test": "src/bin/start.js", + "start-test": "src/bin/start.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/start-server-and-test/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/start-server-and-test/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/start-server-and-test/node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/start-server-and-test/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "duplexer": "~0.1.1" + } + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -4465,6 +6527,16 @@ "node": ">=8" } }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -4632,6 +6704,23 @@ "node": ">=0.8" } }, + "node_modules/throttleit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.1.tgz", + "integrity": "sha512-vDZpf9Chs9mAdfY046mcPt8fg5QSZr37hEH4TXYBnDF+izxgrbRGUAAaBvIk/fJm9aOFCGFd1EsNg5AZCbnQCQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true, + "license": "MIT" + }, "node_modules/tiny-case": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz", @@ -4644,6 +6733,36 @@ "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", "license": "MIT" }, + "node_modules/tldts": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.86" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -4663,6 +6782,29 @@ "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==", "license": "MIT" }, + "node_modules/tough-cookie": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, "node_modules/ts-api-utils": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", @@ -4683,6 +6825,33 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "dev": true, + "license": "Unlicense" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -4730,6 +6899,26 @@ "dev": true, "license": "MIT" }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/update-browserslist-db": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", @@ -4778,6 +6967,31 @@ "dev": true, "license": "MIT" }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, "node_modules/victory-vendor": { "version": "36.9.2", "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", @@ -4856,6 +7070,26 @@ } } }, + "node_modules/wait-on": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-8.0.3.tgz", + "integrity": "sha512-nQFqAFzZDeRxsu7S3C7LbuxslHhk+gnJZHyethuGKAn2IVleIbTB9I3vJSQiSR+DifUqmdzfPMoMPJfLqMF2vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "axios": "^1.8.2", + "joi": "^17.13.3", + "lodash": "^4.17.21", + "minimist": "^1.2.8", + "rxjs": "^7.8.2" + }, + "bin": { + "wait-on": "bin/wait-on" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -5010,6 +7244,17 @@ "node": ">= 14.6" } }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 7989760..832fec2 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,12 @@ "build": "tsc && vite build", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "preview": "vite preview", - "swagger": "python3 -m http.server 8000 && open http://localhost:8000/swagger-ui.html" + "swagger": "python3 -m http.server 8000 && open http://localhost:8000/swagger-ui.html", + "cypress:open": "cypress open", + "cypress:run": "cypress run", + "cypress:run:headless": "cypress run --headless", + "test:e2e": "start-server-and-test dev http://localhost:5173/ cypress:run", + "test:e2e:open": "start-server-and-test dev http://localhost:5173/ cypress:open" }, "dependencies": { "@headlessui/react": "^1.7.17", @@ -30,6 +35,8 @@ "zustand": "^5.0.5" }, "devDependencies": { + "@cypress/react18": "^2.0.1", + "@cypress/vite-dev-server": "^6.0.3", "@types/node": "^24.0.0", "@types/react": "^18.2.15", "@types/react-dom": "^18.2.7", @@ -37,10 +44,12 @@ "@typescript-eslint/parser": "^6.0.0", "@vitejs/plugin-react": "^4.0.3", "autoprefixer": "^10.4.14", + "cypress": "^14.5.3", "eslint": "^8.45.0", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.3", "postcss": "^8.4.27", + "start-server-and-test": "^2.0.12", "tailwindcss": "^3.3.3", "typescript": "^5.0.2", "vite": "^4.4.5" diff --git a/src/App.tsx b/src/App.tsx index 710909c..3965b19 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -42,6 +42,7 @@ import CategoryFormPage from './pages/categories/category-form/CategoryFormPage' // Products Pages import ProductsListPage from './pages/products/products-list/ProductsListPage'; import ProductFormPage from './pages/products/product-form/ProductFormPage'; +import ProductDetailPage from './pages/products/product-detail/ProductDetailPage'; const ProtectedRoute = ({ children }: { children: any }) => { const { user, isLoading } = useAuth(); @@ -102,6 +103,7 @@ const AppRoutes = () => { {/* Products Routes */} } /> + } /> } /> diff --git a/src/components/charts/BarChart.tsx b/src/components/charts/BarChart.tsx index 0c13f62..798798c 100644 --- a/src/components/charts/BarChart.tsx +++ b/src/components/charts/BarChart.tsx @@ -1,44 +1,52 @@ import { BarChart as RechartsBarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts'; -import { ChartData } from '../../types'; +import { CardTitle } from '../ui/Typography'; interface BarChartProps { - data: ChartData[]; + data: any[]; title?: string; color?: string; } export const BarChart = ({ data, title, color = '#3b82f6' }: BarChartProps) => { return ( -
+
{title && ( -

+ {title} -

+ )} - - - - - - - - - +
+ + + + + + + + + +
); }; \ No newline at end of file diff --git a/src/components/charts/LineChart.tsx b/src/components/charts/LineChart.tsx index fa50088..2290ddd 100644 --- a/src/components/charts/LineChart.tsx +++ b/src/components/charts/LineChart.tsx @@ -1,44 +1,59 @@ import { LineChart as RechartsLineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts'; -import { ChartData } from '../../types'; +import { CardTitle } from '../ui/Typography'; interface LineChartProps { - data: ChartData[]; + data: any[]; title?: string; color?: string; } export const LineChart = ({ data, title, color = '#10b981' }: LineChartProps) => { return ( -
+
{title && ( -

+ {title} -

+ )} - - - - - - - - - +
+ + + + + + + + + +
); }; \ No newline at end of file diff --git a/src/components/charts/PieChart.tsx b/src/components/charts/PieChart.tsx index a549e83..052f3f7 100644 --- a/src/components/charts/PieChart.tsx +++ b/src/components/charts/PieChart.tsx @@ -1,8 +1,8 @@ -import { PieChart as RechartsPieChart, Pie, Cell, ResponsiveContainer, Tooltip } from 'recharts'; -import { ChartData } from '../../types'; +import { PieChart as RechartsPieChart, Pie, Cell, Tooltip, ResponsiveContainer, Legend } from 'recharts'; +import { CardTitle } from '../ui/Typography'; interface PieChartProps { - data: ChartData[]; + data: any[]; title?: string; colors?: string[]; } @@ -10,40 +10,71 @@ interface PieChartProps { const DEFAULT_COLORS = ['#3b82f6', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6']; export const PieChart = ({ data, title, colors = DEFAULT_COLORS }: PieChartProps) => { + // Custom legend component for better mobile experience + const CustomLegend = (props: any) => { + const { payload } = props; + return ( +
+ {payload.map((entry: any, index: number) => ( +
+
+ + {entry.value}: {entry.payload.value} + +
+ ))} +
+ ); + }; + return ( -
+
{title && ( -

+ {title} -

+ )} - - - `${name} ${(percent * 100).toFixed(0)}%`} - outerRadius={80} - fill="#8884d8" - dataKey="value" - > - {data.map((_, index) => ( - - ))} - - - - +
+ + + + {data.map((_, index) => ( + + ))} + + [`${value}`, name]} + /> + } + wrapperStyle={{ + paddingTop: '10px' + }} + /> + + +
); }; \ No newline at end of file diff --git a/src/components/dashboard/StatsCard.tsx b/src/components/dashboard/StatsCard.tsx index e33c6cb..a7e5d0e 100644 --- a/src/components/dashboard/StatsCard.tsx +++ b/src/components/dashboard/StatsCard.tsx @@ -1,4 +1,5 @@ import { TrendingUp, TrendingDown } from 'lucide-react'; +import { StatValue, StatLabel } from '../ui/Typography'; interface StatsCardProps { title: string; @@ -27,31 +28,31 @@ export const StatsCard = ({ const isNegative = change && change < 0; return ( -
+
-
- +
+
-
+
-
+ {title} -
+
-
+ {typeof value === 'number' ? value.toLocaleString() : value} -
+ {change !== undefined && ( -
- {isPositive && } - {isNegative && } + {isPositive && } + {isNegative && } {isPositive ? 'افزایش' : 'کاهش'} - {Math.abs(change)}% + {Math.abs(change)}%
)}
diff --git a/src/components/forms/UserForm.tsx b/src/components/forms/UserForm.tsx index ed6b169..3b4d884 100644 --- a/src/components/forms/UserForm.tsx +++ b/src/components/forms/UserForm.tsx @@ -1,126 +1,76 @@ import { useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; -import { User, Phone, Mail, UserCircle } from 'lucide-react'; -import { Input } from '../ui/Input'; +import * as yup from 'yup'; +import { User, Mail, Phone } from 'lucide-react'; import { Button } from '../ui/Button'; -import { userSchema, UserFormData } from '../../utils/validationSchemas'; +import { Input } from '../ui/Input'; +import { UserFormData } from '../../utils/validationSchemas'; + +const userSchema = yup.object({ + name: yup.string().required('نام الزامی است'), + email: yup.string().email('ایمیل معتبر نیست').required('ایمیل الزامی است'), + phone: yup.string().required('شماره تلفن الزامی است'), +}); interface UserFormProps { - initialData?: Partial; onSubmit: (data: UserFormData) => void; - onCancel: () => void; - loading?: boolean; - isEdit?: boolean; + defaultValues?: Partial; + isLoading?: boolean; } -export const UserForm = ({ - initialData, - onSubmit, - onCancel, - loading = false, - isEdit = false -}: UserFormProps) => { +export const UserForm = ({ onSubmit, defaultValues, isLoading }: UserFormProps) => { const { register, handleSubmit, - formState: { errors, isValid }, + formState: { errors, isValid } } = useForm({ - resolver: yupResolver(userSchema) as any, - mode: 'onChange', - defaultValues: initialData, + resolver: yupResolver(userSchema), + defaultValues, + mode: 'onChange' }); return ( -
+

- {isEdit ? 'ویرایش کاربر' : 'افزودن کاربر جدید'} + اطلاعات کاربر

- اطلاعات کاربر را وارد کنید + لطفا اطلاعات کاربر را کامل کنید

-
-
- + + - + - + -
- -
-
- -
- -
- {errors.role && ( -

- {errors.role.message} -

- )} -
-
- - {!isEdit && ( - - )} - -
- +
diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx index 185a47e..a13c472 100644 --- a/src/components/layout/Header.tsx +++ b/src/components/layout/Header.tsx @@ -2,6 +2,7 @@ import { Menu, Sun, Moon, Bell, User, LogOut } from 'lucide-react'; import { useState } from 'react'; import { useAuth } from '../../contexts/AuthContext'; import { useTheme } from '../../contexts/ThemeContext'; +import { SectionTitle } from '../ui/Typography'; interface HeaderProps { onMenuClick: () => void; @@ -15,19 +16,17 @@ export const Header = ({ onMenuClick }: HeaderProps) => { return (
-
+
-

- خوش آمدید -

+ خوش آمدید
-
+
diff --git a/src/components/layout/Layout.tsx b/src/components/layout/Layout.tsx index 120edca..a282f1f 100644 --- a/src/components/layout/Layout.tsx +++ b/src/components/layout/Layout.tsx @@ -7,17 +7,19 @@ export const Layout = () => { const [sidebarOpen, setSidebarOpen] = useState(false); return ( -
+
setSidebarOpen(false)} /> -
+
setSidebarOpen(true)} />
- +
+ +
diff --git a/src/components/layout/Sidebar.tsx b/src/components/layout/Sidebar.tsx index 9363c9e..8b21fa8 100644 --- a/src/components/layout/Sidebar.tsx +++ b/src/components/layout/Sidebar.tsx @@ -8,13 +8,15 @@ import { Key, LogOut, ChevronDown, - ChevronRight, + ChevronLeft, Package, FolderOpen, - Sliders + Sliders, + X } from 'lucide-react'; import { useAuth } from '../../contexts/AuthContext'; import { PermissionWrapper } from '../common/PermissionWrapper'; +import { SectionTitle, SmallText } from '../ui/Typography'; interface MenuItem { title: string; @@ -77,9 +79,14 @@ const menuItems: MenuItem[] = [ } ]; -export const Sidebar = () => { +interface SidebarProps { + isOpen: boolean; + onClose: () => void; +} + +export const Sidebar = ({ isOpen, onClose }: SidebarProps) => { const { user, logout, hasPermission } = useAuth(); - const [expandedItems, setExpandedItems] = React.useState(['مدیریت محصولات', 'مدیریت سیستم']); + const [expandedItems, setExpandedItems] = React.useState([]); const toggleExpanded = (title: string) => { setExpandedItems(prev => @@ -108,7 +115,7 @@ export const Sidebar = () => { {isExpanded ? ( ) : ( - + )} {isExpanded && item.children && ( @@ -123,10 +130,16 @@ export const Sidebar = () => { const menuContent = ( { + // Close mobile menu when clicking a link + if (window.innerWidth < 1024) { + onClose(); + } + }} className={({ isActive }) => `w-full flex items-center px-4 py-3 text-sm font-medium rounded-lg transition-colors ${isActive ? 'bg-primary-50 dark:bg-primary-900 text-primary-600 dark:text-primary-400' - : 'text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700' + : 'text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 hover:text-gray-900 dark:hover:text-white' }` } style={{ paddingLeft: `${paddingLeft + 16}px` }} @@ -148,43 +161,73 @@ export const Sidebar = () => { }; return ( -
- {/* Logo */} -
-

- پنل مدیریت -

-
+ <> + {/* Mobile overlay */} + {isOpen && ( +
+ )} - {/* Navigation */} - - - {/* User Info */} -
-
-
- - {user?.first_name?.[0]}{user?.last_name?.[0]} - -
-
-

- {user?.first_name} {user?.last_name} -

-

- {user?.username} -

-
+ {/* Sidebar */} +
+ {/* Mobile close button */} +
+ + پنل مدیریت +
+ + {/* Logo - desktop only */} +
+ + پنل مدیریت + +
+ + {/* Navigation */} + + + {/* User Info */} +
+
+
+ + {user?.first_name?.[0]}{user?.last_name?.[0]} + +
+
+ + {user?.first_name} {user?.last_name} + + + {user?.username} + +
+ +
+
-
+ ); }; \ No newline at end of file diff --git a/src/components/ui/Input.tsx b/src/components/ui/Input.tsx index 7c87d6d..acc0b68 100644 --- a/src/components/ui/Input.tsx +++ b/src/components/ui/Input.tsx @@ -1,54 +1,49 @@ -import { forwardRef } from 'react'; +import React from 'react'; import { clsx } from 'clsx'; +import { Label } from './Typography'; -interface InputProps { +interface InputProps extends Omit, 'size'> { label?: string; error?: string; - type?: string; - placeholder?: string; - className?: string; - icon?: any; - disabled?: boolean; + helperText?: string; + inputSize?: 'sm' | 'md' | 'lg'; } -export const Input = forwardRef( - ({ label, error, type = 'text', placeholder, className, icon: Icon, disabled, ...props }, ref) => { +export const Input = React.forwardRef( + ({ label, error, helperText, inputSize = 'md', className, id, ...props }, ref) => { + const sizeClasses = { + sm: 'px-3 py-2 text-sm', + md: 'px-3 py-3 text-base', + lg: 'px-4 py-4 text-lg' + }; + + const inputClasses = clsx( + 'w-full border rounded-lg transition-all duration-200 focus:outline-none focus:ring-2', + sizeClasses[inputSize], + error + ? 'border-red-300 focus:border-red-500 focus:ring-red-500' + : 'border-gray-300 dark:border-gray-600 focus:border-primary-500 focus:ring-primary-500', + 'bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100', + 'placeholder-gray-500 dark:placeholder-gray-400', + className + ); + return (
- {label && ( - + {label && } + + {helperText && !error && ( +

{helperText}

)} -
- {Icon && ( -
- -
- )} - -
{error && ( -

- {error} -

+

{error}

)}
); } -); - -Input.displayName = 'Input'; \ No newline at end of file +); \ No newline at end of file diff --git a/src/components/ui/Modal.tsx b/src/components/ui/Modal.tsx index cc17e9b..9b06e82 100644 --- a/src/components/ui/Modal.tsx +++ b/src/components/ui/Modal.tsx @@ -1,12 +1,16 @@ -import { useEffect } from 'react'; +import React, { useEffect } from 'react'; import { X } from 'lucide-react'; +import { Button } from './Button'; +import { SectionSubtitle } from './Typography'; interface ModalProps { isOpen: boolean; onClose: () => void; - title?: string; - children: any; + title: string; + children: React.ReactNode; size?: 'sm' | 'md' | 'lg' | 'xl'; + showCloseButton?: boolean; + actions?: React.ReactNode; } export const Modal = ({ @@ -14,7 +18,9 @@ export const Modal = ({ onClose, title, children, - size = 'md' + size = 'md', + showCloseButton = true, + actions }: ModalProps) => { useEffect(() => { const handleEscape = (e: KeyboardEvent) => { @@ -40,7 +46,7 @@ export const Modal = ({ sm: 'max-w-md', md: 'max-w-lg', lg: 'max-w-2xl', - xl: 'max-w-4xl', + xl: 'max-w-4xl' }; return ( @@ -52,26 +58,31 @@ export const Modal = ({ />
- {title && ( -
-

- {title} -

+ relative w-full ${sizeClasses[size]} + bg-white dark:bg-gray-800 rounded-lg shadow-xl + transform transition-all + `}> +
+ {title} + {showCloseButton && ( -
- )} + )} +
-
+
{children}
+ + {actions && ( +
+ {actions} +
+ )}
diff --git a/src/index.css b/src/index.css index 7dd6a2e..b5bc0fb 100644 --- a/src/index.css +++ b/src/index.css @@ -30,11 +30,31 @@ body { background-color: #f9fafb; transition: background-color 0.2s ease; + /* Prevent horizontal scrolling on mobile */ + overflow-x: hidden; } .dark body { background-color: #111827; } + + /* Ensure touch targets are large enough on mobile */ + @media (max-width: 1024px) { + button, + a, + [role="button"] { + min-height: 44px; + min-width: 44px; + } + } + + /* Improve text selection on mobile */ + @media (max-width: 768px) { + * { + -webkit-text-size-adjust: 100%; + -webkit-tap-highlight-color: transparent; + } + } } @layer components { @@ -53,4 +73,76 @@ .input { @apply w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-primary-500 focus:border-transparent transition-colors duration-200; } + + /* Mobile-specific utilities */ + .mobile-container { + @apply px-4 sm:px-6 lg:px-8; + } + + .mobile-card { + @apply card p-4 sm:p-6; + } + + /* Safe area for mobile devices */ + .safe-area { + padding-left: env(safe-area-inset-left); + padding-right: env(safe-area-inset-right); + padding-top: env(safe-area-inset-top); + padding-bottom: env(safe-area-inset-bottom); + } + + /* Mobile-specific form improvements */ + @media (max-width: 768px) { + .input, + textarea, + select { + @apply text-base; /* Prevent zoom on iOS */ + font-size: 16px !important; + } + + .form-grid { + @apply grid-cols-1 gap-4; + } + + .button-group { + @apply flex-col space-y-3 space-x-0; + } + + .button-group > * { + @apply w-full; + } + } + + /* Responsive text utilities */ + .text-responsive-xs { + @apply text-xs sm:text-sm; + } + + .text-responsive-sm { + @apply text-sm sm:text-base; + } + + .text-responsive-base { + @apply text-base sm:text-lg; + } + + .text-responsive-lg { + @apply text-lg sm:text-xl lg:text-2xl; + } + + .text-responsive-xl { + @apply text-xl sm:text-2xl lg:text-3xl; + } + + /* Mobile chart container */ + .chart-container { + @apply w-full overflow-hidden; + min-height: 200px; + } + + @media (max-width: 640px) { + .chart-container { + min-height: 180px; + } + } } diff --git a/src/pages/Dashboard.tsx b/src/pages/Dashboard.tsx index 08a8b66..88de42a 100644 --- a/src/pages/Dashboard.tsx +++ b/src/pages/Dashboard.tsx @@ -1,4 +1,4 @@ -import { Users, ShoppingBag, DollarSign, TrendingUp } from 'lucide-react'; +import { Users, ShoppingBag, DollarSign, TrendingUp, BarChart3, Plus } from 'lucide-react'; import { StatsCard } from '../components/dashboard/StatsCard'; import { BarChart } from '../components/charts/BarChart'; import { LineChart } from '../components/charts/LineChart'; @@ -6,6 +6,7 @@ import { PieChart } from '../components/charts/PieChart'; import { Table } from '../components/ui/Table'; import { Button } from '../components/ui/Button'; import { PermissionWrapper } from '../components/common/PermissionWrapper'; +import { PageContainer, PageTitle, CardTitle } from '../components/ui/Typography'; import { ChartData, TableColumn } from '../types'; const statsData = [ @@ -98,55 +99,69 @@ const userColumns: TableColumn[] = [ export const Dashboard = () => { return ( -
-
-

- داشبورد -

-
- + + {/* Header with mobile-responsive layout */} +
+ داشبورد +
+ - +
-
+ {/* Stats Cards - Mobile responsive grid */} +
{statsData.map((stat, index) => ( ))}
-
- - + {/* Charts - Better mobile layout */} +
+
+ +
+
+ +
-
-
-
-

+ {/* Table and Pie Chart - Mobile responsive */} +
+
+
+ کاربران اخیر -

- + +
+
+ -
+
{ />
- + ); }; \ No newline at end of file diff --git a/src/pages/Notifications.tsx b/src/pages/Notifications.tsx index c9dc233..a8251c8 100644 --- a/src/pages/Notifications.tsx +++ b/src/pages/Notifications.tsx @@ -1,7 +1,11 @@ import { useState } from 'react'; -import { Bell, Check, X, Plus, Search, AlertCircle, Info, CheckCircle, XCircle } from 'lucide-react'; +import { Plus, Search, Filter, Bell, BellOff, Clock, Eye } from 'lucide-react'; +import { Table } from '../components/ui/Table'; import { Button } from '../components/ui/Button'; import { Pagination } from '../components/ui/Pagination'; +import { PermissionWrapper } from '../components/common/PermissionWrapper'; +import { TableColumn } from '../types'; +import { PageContainer, PageTitle, StatValue } from '../components/ui/Typography'; const allNotifications = [ { @@ -96,13 +100,13 @@ export const Notifications = () => { const getNotificationIcon = (type: string) => { switch (type) { case 'error': - return ; + return ; case 'warning': - return ; + return ; case 'success': - return ; + return ; case 'info': - return ; + return ; default: return ; } @@ -156,31 +160,25 @@ export const Notifications = () => { const unreadCount = notifications.filter(n => !n.isRead).length; return ( -
-
-
-

- اعلانات -

-

- {unreadCount} اعلان خوانده نشده از {notifications.length} اعلان -

-
+ + اعلانات + + {unreadCount} اعلان خوانده نشده از {notifications.length} اعلان + -
- - -
+
+ +
@@ -189,41 +187,41 @@ export const Notifications = () => {

کل اعلانات

-

{notifications.length}

+ {notifications.length}
- +

خوانده نشده

-

{unreadCount}

+ {unreadCount}
- +

خطا

-

+ {notifications.filter(n => n.type === 'error').length} -

+
- +

هشدار

-

+ {notifications.filter(n => n.type === 'warning').length} -

+
@@ -308,7 +306,7 @@ export const Notifications = () => { variant="secondary" onClick={() => handleMarkAsRead(notification.id)} > - + )}
@@ -339,6 +337,6 @@ export const Notifications = () => { totalItems={filteredNotifications.length} /> - + ); }; \ No newline at end of file diff --git a/src/pages/Orders.tsx b/src/pages/Orders.tsx index ca5ede6..2abea74 100644 --- a/src/pages/Orders.tsx +++ b/src/pages/Orders.tsx @@ -1,9 +1,11 @@ import { useState } from 'react'; -import { Search, Filter, ShoppingCart, TrendingUp } from 'lucide-react'; +import { Plus, Search, Filter, Package, ShoppingCart, DollarSign, Clock } from 'lucide-react'; import { Table } from '../components/ui/Table'; import { Button } from '../components/ui/Button'; import { Pagination } from '../components/ui/Pagination'; +import { PermissionWrapper } from '../components/common/PermissionWrapper'; import { TableColumn } from '../types'; +import { PageContainer, PageTitle, StatValue } from '../components/ui/Typography'; const allOrders = [ { id: 1001, customer: 'علی احمدی', products: '۳ محصول', amount: '۴۵,۰۰۰,۰۰۰', status: 'تحویل شده', date: '۱۴۰۲/۰۸/۱۵' }, @@ -100,24 +102,11 @@ export const Orders = () => { }, 0); return ( -
-
-
-

- مدیریت سفارشات -

-

- {filteredOrders.length} سفارش یافت شد -

-
- -
- -
-
+ + مدیریت سفارشات +

+ {filteredOrders.length} سفارش یافت شد +

@@ -125,19 +114,19 @@ export const Orders = () => {

کل سفارشات

-

{allOrders.length}

+ {allOrders.length}
- +

تحویل شده

-

+ {allOrders.filter(o => o.status === 'تحویل شده').length} -

+
@@ -156,7 +145,7 @@ export const Orders = () => {
- +

کل فروش

@@ -198,6 +187,6 @@ export const Orders = () => { />

-
+
); }; \ No newline at end of file diff --git a/src/pages/Products.tsx b/src/pages/Products.tsx index 1e681c5..466b394 100644 --- a/src/pages/Products.tsx +++ b/src/pages/Products.tsx @@ -1,10 +1,12 @@ -import { useState } from 'react'; -import { Plus, Search, Filter, Package } from 'lucide-react'; +import React, { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { Package, Plus, Search, Filter, Eye, Edit, Trash2, Grid, List } from 'lucide-react'; import { Table } from '../components/ui/Table'; import { Button } from '../components/ui/Button'; import { Pagination } from '../components/ui/Pagination'; import { PermissionWrapper } from '../components/common/PermissionWrapper'; import { TableColumn } from '../types'; +import { PageContainer, PageTitle, StatValue } from '../components/ui/Typography'; const allProducts = [ { id: 1, name: 'لپ‌تاپ ایسوس', category: 'کامپیوتر', price: '۲۵,۰۰۰,۰۰۰', stock: 15, status: 'موجود', createdAt: '۱۴۰۲/۰۸/۱۵' }, @@ -103,29 +105,23 @@ const Products = () => { }; return ( -
-
-
-

- مدیریت محصولات -

-

- {filteredProducts.length} محصول یافت شد -

-
+ + مدیریت محصولات +

+ {filteredProducts.length} محصول یافت شد +

-
- + + - - - -
+
@@ -134,7 +130,7 @@ const Products = () => {

کل محصولات

-

{allProducts.length}

+ {allProducts.length}
@@ -144,9 +140,9 @@ const Products = () => {

محصولات موجود

-

+ {allProducts.filter(p => p.status === 'موجود').length} -

+
@@ -156,9 +152,9 @@ const Products = () => {

محصولات ناموجود

-

+ {allProducts.filter(p => p.status === 'ناموجود').length} -

+
@@ -195,7 +191,7 @@ const Products = () => { /> - + ); }; diff --git a/src/pages/Users.tsx b/src/pages/Users.tsx index 3b90871..6553fad 100644 --- a/src/pages/Users.tsx +++ b/src/pages/Users.tsx @@ -1,5 +1,5 @@ import { useState } from 'react'; -import { Plus, Search, Filter } from 'lucide-react'; +import { Plus, Search, Filter, Users as UsersIcon, UserCheck, UserX } from 'lucide-react'; import { Table } from '../components/ui/Table'; import { Button } from '../components/ui/Button'; import { Modal } from '../components/ui/Modal'; @@ -8,6 +8,7 @@ import { UserForm } from '../components/forms/UserForm'; import { PermissionWrapper } from '../components/common/PermissionWrapper'; import { TableColumn } from '../types'; import { UserFormData } from '../utils/validationSchemas'; +import { PageContainer, PageTitle, StatValue } from '../components/ui/Typography'; const allUsers = [ { id: 1, name: 'علی احمدی', email: 'ali@example.com', role: 'کاربر', status: 'فعال', createdAt: '۱۴۰۲/۰۸/۱۵', phone: '۰۹۱۲۳۴۵۶۷۸۹' }, @@ -111,27 +112,28 @@ export const Users = () => { }; return ( -
+
-

- مدیریت کاربران -

+ مدیریت کاربران

{filteredUsers.length} کاربر یافت شد

-
+
- +
@@ -181,6 +183,6 @@ export const Users = () => { isEdit={!!editingUser} /> -
+
); }; \ No newline at end of file diff --git a/src/pages/UsersNew.tsx b/src/pages/UsersNew.tsx index e61ee8c..14d8134 100644 --- a/src/pages/UsersNew.tsx +++ b/src/pages/UsersNew.tsx @@ -176,19 +176,20 @@ const Users = () => {

-
+
- + +
diff --git a/src/pages/admin-users/admin-user-form/AdminUserFormPage.tsx b/src/pages/admin-users/admin-user-form/AdminUserFormPage.tsx index bc2c06c..b012408 100644 --- a/src/pages/admin-users/admin-user-form/AdminUserFormPage.tsx +++ b/src/pages/admin-users/admin-user-form/AdminUserFormPage.tsx @@ -12,6 +12,7 @@ import { Input } from "@/components/ui/Input"; import { LoadingSpinner } from "@/components/ui/LoadingSpinner"; import { MultiSelectAutocomplete, Option } from "@/components/ui/MultiSelectAutocomplete"; import { ArrowRight } from "lucide-react"; +import { FormHeader, PageContainer, Label } from '../../../components/ui/Typography'; const adminUserSchema = yup.object({ first_name: yup.string().required('نام الزامی است').min(2, 'نام باید حداقل 2 کاراکتر باشد'), @@ -38,7 +39,7 @@ const AdminUserFormPage = () => { const { data: user, isLoading: isLoadingUser } = useAdminUser(id || '', isEdit); const { mutate: createUser, isPending: isCreating } = useCreateAdminUser(); const { mutate: updateUser, isPending: isUpdating } = useUpdateAdminUser(); - + const { data: permissions, isLoading: isLoadingPermissions } = usePermissions(); const { data: roles, isLoading: isLoadingRoles } = useRoles(); @@ -139,31 +140,28 @@ const AdminUserFormPage = () => { ); } + const backButton = ( + + ); + return ( -
- {/* Header */} -
- -
-

- {isEdit ? 'ویرایش کاربر ادمین' : 'ایجاد کاربر ادمین جدید'} -

-

- {isEdit ? 'ویرایش اطلاعات کاربر ادمین' : 'اطلاعات کاربر ادمین جدید را وارد کنید'} -

-
-
+ + {/* Form */} -
-
+
+
{ isLoading={isLoadingPermissions} error={errors.permissions?.message} /> - + ({ @@ -259,7 +257,7 @@ const AdminUserFormPage = () => {
-
+
); }; diff --git a/src/pages/admin-users/admin-users-list/AdminUsersListPage.tsx b/src/pages/admin-users/admin-users-list/AdminUsersListPage.tsx index e01a741..dc2cc6c 100644 --- a/src/pages/admin-users/admin-users-list/AdminUsersListPage.tsx +++ b/src/pages/admin-users/admin-users-list/AdminUsersListPage.tsx @@ -6,6 +6,7 @@ import { Button } from "@/components/ui/Button"; import { LoadingSpinner } from "@/components/ui/LoadingSpinner"; import { Trash2, Edit3, Plus, Eye, Users, UserPlus } from "lucide-react"; import { Modal } from "@/components/ui/Modal"; +import { PageContainer, PageTitle, SectionSubtitle } from '../../../components/ui/Typography'; // Skeleton Loading Component const AdminUserTableSkeleton = () => ( @@ -134,25 +135,28 @@ const AdminUsersListPage = () => { } return ( -
+ {/* Header */} -
+
-

+
- مدیریت کاربران ادمین -

-

- مدیریت کاربران دسترسی به پنل ادمین -

+ مدیریت کاربران ادمین +
+

مدیریت کاربران دسترسی به پنل ادمین

- + +
{/* Filters */} + فیلترها
@@ -242,8 +246,8 @@ const AdminUsersListPage = () => {
{user.status === 'active' ? 'فعال' : 'غیرفعال'} @@ -297,8 +301,8 @@ const AdminUsersListPage = () => {

{user.status === 'active' ? 'فعال' : 'غیرفعال'} @@ -363,7 +367,7 @@ const AdminUsersListPage = () => { - + ); }; diff --git a/src/pages/permissions/permission-form/PermissionFormPage.tsx b/src/pages/permissions/permission-form/PermissionFormPage.tsx index 8192a4a..701a373 100644 --- a/src/pages/permissions/permission-form/PermissionFormPage.tsx +++ b/src/pages/permissions/permission-form/PermissionFormPage.tsx @@ -9,6 +9,7 @@ import { Button } from "@/components/ui/Button"; import { Input } from "@/components/ui/Input"; import { LoadingSpinner } from "@/components/ui/LoadingSpinner"; import { ArrowRight } from "lucide-react"; +import { FormHeader, PageContainer, Label } from '../../../components/ui/Typography'; const permissionSchema = yup.object({ title: yup.string().required('عنوان الزامی است').min(3, 'عنوان باید حداقل 3 کاراکتر باشد'), @@ -87,26 +88,22 @@ const PermissionFormPage = () => { } return ( -
+ {/* Header */} -
- -
-

- {isEdit ? 'ویرایش دسترسی' : 'ایجاد دسترسی جدید'} -

-

- {isEdit ? 'ویرایش اطلاعات دسترسی' : 'اطلاعات دسترسی جدید را وارد کنید'} -

-
-
+ + + بازگشت + + } + /> {/* Form */}
@@ -119,9 +116,9 @@ const PermissionFormPage = () => { />
-