Skip to content

Commit

Permalink
[DLT-1110] Update chai with mocha, attempt usage of mock-import
Browse files Browse the repository at this point in the history
  • Loading branch information
AronPerez committed Jan 6, 2025
1 parent 500ba5b commit 1567afb
Show file tree
Hide file tree
Showing 5 changed files with 562 additions and 36 deletions.
120 changes: 84 additions & 36 deletions web/test/components/transfer/transfer-dialog-controller.test.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,89 @@
import { expect } from "chai";
import sinon from "sinon";
import { expect, sinon } from "../../setup.js";
import { TransferDialogController } from "../../../static/components/transfer/transfer-dialog-controller.js";
import { TransferModel } from "../../../static/models/transfer-model.js";
import { TransferEndpointManager } from "../../../static/components/transfer/transfer-endpoint-manager.js";
import { TransferModel } from "../../../static/models/transfer-model.js";
import { TransferUIManager } from "../../../static/components/transfer/transfer-ui-manager.js";
import * as dialogs from "../../../static/dialogs.js";
import { createMockImport } from "mock-import";
import { createMockModule } from "../../test-utils.js";

describe("TransferDialogController", () => {
let controller;
let mockCallback;
let mockDialogs;
let sandbox;

const TEST_MODE = 1;
const TEST_IDS = [{ id: 1 }, { id: 2 }];

beforeEach(() => {
mockCallback = sinon.spy();
sinon.stub(TransferModel.prototype);
sinon.stub(TransferEndpointManager.prototype);
sinon.stub(TransferUIManager.prototype, "createDialog");
sinon.stub(TransferUIManager.prototype, "initializeComponents");
sinon.stub(TransferUIManager.prototype, "attachMatchesHandler");
sinon.stub(TransferUIManager.prototype, "showDialog");
sinon.stub(dialogs, "dlgAlert");
beforeEach(async () => {
sandbox = sinon.createSandbox();
mockCallback = sandbox.stub();

const MockTransferModel = class {
constructor() {
this.cache = new Map();
}
setCache() {}
};

const MockEndpointManager = class {
constructor() {
this.initialized = false;
}
initialize() {
this.initialized = true;
}
};

const MockUIManager = class {
constructor() {
this.dialog = {};
this.button = {};
}
createDialog() {}
initializeComponents() {}
attachMatchesHandler() {}
showDialog() {}
getDialogLabels() {
return {};
}
reInitializeUIComponents() {}
};

mockDialogs = {
dlgAlert: sandbox.stub(),
};

const mockImporter = createMockImport(import.meta.url);

const mocks = {
"../../static/dialogs.js": createMockModule(mockDialogs),
"../../models/transfer-model.js": createMockModule({
TransferModel: MockTransferModel,
}),
"./transfer-endpoint-manager.js": createMockModule({
TransferEndpointManager: MockEndpointManager,
}),
"./transfer-ui-manager.js": createMockModule({
TransferUIManager: MockUIManager,
}),
};

// Apply all mocks
for (const [path, mock] of Object.entries(mocks)) {
await mockImporter.mockImport(path, mock);
}

const module = await mockImporter.reImport(
"../../../static/components/transfer/transfer-dialog-controller.js",
);

TransferDialogController = module.TransferDialogController;
controller = new TransferDialogController(TEST_MODE, TEST_IDS, mockCallback);
});

afterEach(() => {
sinon.restore();
sandbox.restore();
});

describe("constructor", () => {
Expand All @@ -40,34 +97,25 @@ describe("TransferDialogController", () => {
});

describe("show", () => {
it("should successfully show the transfer dialog", () => {
controller.show();
it("should successfully show the transfer dialog", async () => {
await controller.show();

expect(controller.uiManager.createDialog.calledOnce).to.be.true;
expect(controller.uiManager.initializeComponents.calledOnce).to.be.true;
expect(controller.uiManager.attachMatchesHandler.calledOnce).to.be.true;
expect(controller.uiManager.showDialog.calledOnce).to.be.true;
expect(controller.uiManager.createDialog.called).to.be.true;
expect(controller.uiManager.initializeComponents.called).to.be.true;
expect(controller.uiManager.attachMatchesHandler.called).to.be.true;
expect(controller.uiManager.showDialog.called).to.be.true;
expect(controller.endpointManager.initialized).to.be.true;
});

it("should handle errors and show alert dialog", () => {
controller.uiManager.createDialog.throws(new Error("Test error"));
controller.show();
it("should handle errors and show alert dialog", async () => {
const error = new Error("Test error");
controller.uiManager.createDialog.throws(error);

expect(dialogs.dlgAlert.calledOnce).to.be.true;
expect(dialogs.dlgAlert.calledWith("Error", "Failed to open transfer dialog")).to.be
.true;
});

it("should call UI methods in correct order", () => {
controller.show();
await controller.show();

sinon.assert.callOrder(
controller.uiManager.createDialog,
controller.uiManager.initializeComponents,
controller.uiManager.attachMatchesHandler,
controller.uiManager.showDialog,
);
expect(mockDialogs.dlgAlert.calledOnce).to.be.true;
expect(mockDialogs.dlgAlert.calledWith("Error", "Failed to open transfer dialog")).to.be
.true;
});
});
});
148 changes: 148 additions & 0 deletions web/test/components/transfer/transfer-endpoint-manager.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import { expect } from "chai";
import sinon from "sinon";
import { TransferEndpointManager } from "../../../static/components/transfer/transfer-endpoint-manager.js";
import * as api from "../../../static/api.js";
import * as dialogs from "../../../static/dialogs.js";

describe("TransferEndpointManager", () => {
let manager;
let mockDialog;
let mockJQuery;

beforeEach(() => {
mockDialog = {
uiManager: {
frame: $("<div>"),
updateEndpoint: sinon.stub(),
state: { endpointOk: false },
updateButtonStates: sinon.stub(),
createMatchesHtml: sinon.stub().returns("<option>Test</option>"),
},
};

sinon.stub(api, "epView");
sinon.stub(api, "epAutocomplete");

manager = new TransferEndpointManager(mockDialog);
manager.initialized = true;
});

afterEach(() => {
sinon.restore();
});

describe("getEndpointStatus", () => {
it('should return "active" for non-activated endpoint with expires_in = -1', () => {
const endpoint = { activated: false, expires_in: -1 };
expect(manager.getEndpointStatus(endpoint)).to.equal("active");
});

it("should return hours remaining for activated endpoint", () => {
const endpoint = { activated: true, expires_in: 7200 };
expect(manager.getEndpointStatus(endpoint)).to.equal("2 hrs");
});

it('should return "inactive" for non-activated endpoint with expires_in != -1', () => {
const endpoint = { activated: false, expires_in: 0 };
expect(manager.getEndpointStatus(endpoint)).to.equal("inactive");
});
});

describe("searchEndpoint", () => {
let epViewStub;

beforeEach(() => {
epViewStub = sinon.stub(api, "epView");
manager.currentSearchToken = "test-token";
});

it("should update UI on successful direct endpoint match", () => {
const mockData = { name: "test-endpoint" };
epViewStub.callsFake((endpoint, callback) => callback(true, mockData));

manager.searchEndpoint("test-endpoint", "test-token");

expect(manager.controller.uiManager.updateEndpoint.calledWith(mockData)).to.be.true;
expect(manager.controller.uiManager.state.endpointOk).to.be.true;
expect(manager.controller.uiManager.updateButtonStates.called).to.be.true;
});

it("should fall back to autocomplete when no direct match found", () => {
const searchAutocompleteSpy = sinon.spy(manager, "searchEndpointAutocomplete");
epViewStub.callsFake((endpoint, callback) => callback(true, { code: "NOT_FOUND" }));

manager.searchEndpoint("test-endpoint", "test-token");

expect(searchAutocompleteSpy.calledWith("test-endpoint", "test-token")).to.be.true;
});

it("should handle errors gracefully", () => {
const dlgAlertStub = sinon.stub(dialogs, "dlgAlert");
epViewStub.throws(new Error("Test error"));

manager.searchEndpoint("test-endpoint", "test-token");

expect(dlgAlertStub.calledWith("Globus Error", sinon.match.any)).to.be.true;
});
});

describe("searchEndpointAutocomplete", () => {
let epAutocompleteStub;

beforeEach(() => {
epAutocompleteStub = sinon.stub(api, "epAutocomplete");
manager.currentSearchToken = "test-token";
});

it("should update matches list with autocomplete results", () => {
const mockData = {
DATA: [
{ id: "1", canonical_name: "endpoint1" },
{ id: "2", canonical_name: "endpoint2" },
],
};
epAutocompleteStub.callsFake((endpoint, callback) => callback(true, mockData));

manager.searchEndpointAutocomplete("test", "test-token");

expect(manager.endpointManagerList).to.deep.equal(mockData.DATA);
});

it("should handle no matches case", () => {
epAutocompleteStub.callsFake((endpoint, callback) => callback(true, { DATA: [] }));

manager.searchEndpointAutocomplete("test", "test-token");

expect(manager.endpointManagerList).to.be.null;
});
});

describe("handlePathInput", () => {
it("should process valid path input", () => {
const searchEndpointSpy = sinon.spy(manager, "searchEndpoint");
manager.currentSearchToken = "test-token";

manager.handlePathInput("test-token");

expect(searchEndpointSpy.called).to.be.true;
});

it("should handle empty path input", () => {
mockJQuery().val.returns("");
manager.currentSearchToken = "test-token";

manager.handlePathInput("test-token");

expect(manager.endpointManagerList).to.be.null;
});

it("should ignore stale requests", () => {
manager.currentSearchToken = "different-token";
const searchEndpointSpy = sinon.spy(manager, "searchEndpoint");

manager.handlePathInput("test-token");

expect(searchEndpointSpy.called).to.be.false;
});
});
});
Loading

0 comments on commit 1567afb

Please sign in to comment.