diff --git a/spec/controllers/admin/reports_controller_spec.rb b/spec/controllers/admin/reports_controller_spec.rb index d0b6104d8c..2f6404cba6 100644 --- a/spec/controllers/admin/reports_controller_spec.rb +++ b/spec/controllers/admin/reports_controller_spec.rb @@ -370,4 +370,153 @@ RSpec.describe Admin::ReportsController do end end end + + context "AJAX Search" do + let(:enterprise_fee1) { + create(:enterprise_fee, name: "Delivery Fee", enterprise: distributor1) + } + let(:enterprise_fee2) { create(:enterprise_fee, name: "Admin Fee", enterprise: distributor2) } + + before do + controller_login_as_admin + orderA1.adjustments.create!( + originator: enterprise_fee1, + label: "Delivery Fee", + amount: 5.0, + state: "finalized", + order: orderA1 + ) + orderB1.adjustments.create!( + originator: enterprise_fee2, + label: "Admin Fee", + amount: 3.0, + state: "finalized", + order: orderB1 + ) + end + + describe "#search_enterprise_fees" do + it "returns paginated JSON with enterprise fees ordered by name" do + spree_get( + :search_enterprise_fees, + report_type: :enterprise_fee_summary, + report_subtype: :enterprise_fees_with_tax_report_by_order + ) + + expect(response).to have_http_status(:ok) + json_response = response.parsed_body + + names = json_response["results"].pluck("text") + expect(names).to eq(['Admin Fee', 'Delivery Fee']) + end + + it "caches and works with different report types" do + spree_get( + :search_enterprise_fees, + report_type: :enterprise_fee_summary, + report_subtype: :enterprise_fees_with_tax_report_by_order + ) + first_response = response.parsed_body + + spree_get( + :search_enterprise_fees, + report_type: :enterprise_fee_summary, + report_subtype: :enterprise_fees_with_tax_report_by_order + ) + second_response = response.parsed_body + + expect(first_response).to eq(second_response) + end + end + + describe "#search_enterprise_fee_owners" do + it "returns paginated JSON with unique enterprise owners ordered by name" do + distributor1.update!(name: "Zebra Farm") + distributor2.update!(name: "Alpha Market") + + spree_get( + :search_enterprise_fee_owners, + report_type: :enterprise_fee_summary, + report_subtype: :enterprise_fees_with_tax_report_by_order + ) + + expect(response).to have_http_status(:ok) + json_response = response.parsed_body + + names = json_response["results"].pluck("text") + expect(names).to eq(['Alpha Market', 'Zebra Farm']) + end + + it "caches results" do + spree_get( + :search_enterprise_fee_owners, + report_type: :enterprise_fee_summary, + report_subtype: :enterprise_fees_with_tax_report_by_order + ) + first_response = response.parsed_body + + spree_get( + :search_enterprise_fee_owners, + report_type: :enterprise_fee_summary, + report_subtype: :enterprise_fees_with_tax_report_by_order + ) + second_response = response.parsed_body + + expect(first_response).to eq(second_response) + end + end + + describe "#search_order_customers" do + it "filters customers by email and returns paginated results" do + customer1 = create(:customer, email: "alice@example.com", enterprise: distributor1) + customer2 = create(:customer, email: "bob@example.com", enterprise: distributor1) + orderA1.update!(customer: customer1) + orderA2.update!(customer: customer2) + + spree_get( + :search_order_customers, + report_type: :enterprise_fee_summary, + report_subtype: :enterprise_fees_with_tax_report_by_order, + q: "alice" + ) + + json_response = response.parsed_body + expect(json_response["results"].pluck("text")).to eq(["alice@example.com"]) + end + end + + describe "#search_order_cycles" do + it "filters order cycles by name and orders by close date" do + ocA.update!(name: "Winter Market") + ocB.update!(name: "Summer Market") + + spree_get( + :search_order_cycles, + report_type: :enterprise_fee_summary, + report_subtype: :enterprise_fees_with_tax_report_by_order, + q: "Winter" + ) + + json_response = response.parsed_body + expect(json_response["results"].pluck("text")).to eq(["Winter Market"]) + end + end + + describe "#search_distributors" do + it "filters distributors by name" do + distributor1.update!(name: "Alpha Farm") + distributor2.update!(name: "Beta Market") + + spree_get( + :search_distributors, + report_type: :enterprise_fee_summary, + report_subtype: :enterprise_fees_with_tax_report_by_order, + q: "Alpha" + ) + + json_response = response.parsed_body + expect(json_response["results"].pluck("text")).to eq(["Alpha Farm"]) + end + end + end end diff --git a/spec/javascripts/stimulus/select2_ajax_controller_test.js b/spec/javascripts/stimulus/select2_ajax_controller_test.js new file mode 100644 index 0000000000..3b1d449a7a --- /dev/null +++ b/spec/javascripts/stimulus/select2_ajax_controller_test.js @@ -0,0 +1,156 @@ +/** + * @jest-environment jsdom + */ + +import { Application } from "stimulus"; +import select2_ajax_controller from "../../../app/webpacker/controllers/select2_ajax_controller.js"; + +describe("Select2AjaxController", () => { + let select2InitOptions = null; + let application; + + beforeAll(() => { + application = Application.start(); + application.register("select2-ajax", select2_ajax_controller); + }); + + beforeEach(() => { + select2InitOptions = null; + + // Mock jQuery and select2 + const mockVal = jest.fn(function (value) { + if (value !== undefined) { + this._value = value; + return this; + } + return this._value || ""; + }); + + const mockOn = jest.fn().mockReturnThis(); + + const mockSelect2 = jest.fn(function (options) { + if (typeof options === "string" && options === "destroy") { + return this; + } + select2InitOptions = options; + return this; + }); + + const jQueryMock = jest.fn((selector) => { + let element; + if (typeof selector === "string" && selector.startsWith(" + + + `; + }); + + afterEach(() => { + document.body.innerHTML = ""; + delete global.$; + }); + + describe("#connect", () => { + it("initializes select2 with correct AJAX URL", () => { + expect(select2InitOptions).not.toBeNull(); + expect(select2InitOptions.ajax.url).toBe("/api/search"); + }); + + it("configures select2 with correct options", () => { + expect(select2InitOptions.ajax.dataType).toBe("json"); + expect(select2InitOptions.ajax.quietMillis).toBe(300); + expect(select2InitOptions.allowClear).toBe(true); + expect(select2InitOptions.minimumInputLength).toBe(0); + expect(select2InitOptions.width).toBe("100%"); + }); + + it("configures AJAX data function", () => { + const dataFunc = select2InitOptions.ajax.data; + const result = dataFunc("search term", 2); + + expect(result).toEqual({ + q: "search term", + page: 2, + }); + }); + + it("handles empty search term", () => { + const dataFunc = select2InitOptions.ajax.data; + const result = dataFunc(null, null); + + expect(result).toEqual({ + q: "", + page: 1, + }); + }); + + it("configures results function with pagination", () => { + const resultsFunc = select2InitOptions.ajax.results; + const mockData = { + results: [{ id: 1, text: "Item 1" }], + pagination: { more: true }, + }; + + const result = resultsFunc(mockData, 1); + + expect(result).toEqual({ + results: mockData.results, + more: true, + }); + }); + + it("handles missing pagination", () => { + const resultsFunc = select2InitOptions.ajax.results; + const mockData = { + results: [{ id: 1, text: "Item 1" }], + }; + + const result = resultsFunc(mockData, 1); + + expect(result).toEqual({ + results: mockData.results, + more: false, + }); + }); + + it("configures format functions", () => { + const item = { id: 1, text: "Test Item" }; + + expect(select2InitOptions.formatResult(item)).toBe("Test Item"); + expect(select2InitOptions.formatSelection(item)).toBe("Test Item"); + }); + }); +});