From 424f6b523a52ed16839651a1e36b33c985375fbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Arranz?= Date: Fri, 26 Oct 2018 14:02:26 +0200 Subject: [PATCH 01/22] Initial WorkspaceView unit tests --- src/GruntFile.js | 1 + src/js_tests/wirecloud/coreSpec.js | 23 +- .../wirecloud/ui/SharingWindowMenuSpec.js | 5 + .../wirecloud/ui/WorkspaceViewSpec.js | 1010 +++++++++++++++++ .../platform/static/js/wirecloud/Workspace.js | 2 +- .../platform/static/js/wirecloud/core.js | 2 + .../js/wirecloud/ui/WorkspaceListItems.js | 2 +- .../js/wirecloud/ui/WorkspaceTabView.js | 11 + .../static/js/wirecloud/ui/WorkspaceView.js | 194 ++-- .../js/wirecloud/ui/WorkspaceViewMenuItems.js | 6 +- 10 files changed, 1142 insertions(+), 114 deletions(-) create mode 100644 src/js_tests/wirecloud/ui/WorkspaceViewSpec.js diff --git a/src/GruntFile.js b/src/GruntFile.js index 530c9faf51..1c911043c2 100644 --- a/src/GruntFile.js +++ b/src/GruntFile.js @@ -129,6 +129,7 @@ var WirecloudFiles = [ 'wirecloud/platform/static/js/wirecloud/ui/WiringEditor/BehaviourEngine.js', 'wirecloud/platform/static/js/wirecloud/ui/WiringEditor/Endpoint.js', 'wirecloud/platform/static/js/wirecloud/ui/WiringEditor/KeywordSuggestion.js', + 'wirecloud/platform/static/js/wirecloud/ui/WorkspaceView.js', 'wirecloud/platform/static/js/wirecloud/ui/SharingWindowMenu.js', 'wirecloud/platform/static/js/wirecloud/wiring/Endpoint.js', 'wirecloud/platform/static/js/wirecloud/wiring/EndpointTypeError.js', diff --git a/src/js_tests/wirecloud/coreSpec.js b/src/js_tests/wirecloud/coreSpec.js index 6634d4b202..4a671ee40b 100644 --- a/src/js_tests/wirecloud/coreSpec.js +++ b/src/js_tests/wirecloud/coreSpec.js @@ -49,6 +49,7 @@ id: 1, owner: "wirecloud", name: "home", + title: "Home", contextManager: { addCallback: jasmine.createSpy() } @@ -114,7 +115,8 @@ // Act var task = Wirecloud.changeActiveWorkspace({ owner: "wirecloud", - name: "home" + name: "home", + title: "Home" }); // Assert @@ -165,6 +167,7 @@ expect(Wirecloud.HistoryManager.pushState).toHaveBeenCalledWith({ workspace_owner: "wirecloud", workspace_name: "home", + workspace_title: "Home", view: "workspace", tab: "MyTab" }); @@ -190,6 +193,7 @@ expect(Wirecloud.HistoryManager.replaceState).toHaveBeenCalledWith({ workspace_owner: "wirecloud", workspace_name: "home", + workspace_title: "Home", view: "workspace" }); expect(Wirecloud.activeWorkspace).toBe(initworkspace); @@ -465,6 +469,7 @@ Wirecloud.HistoryManager.getCurrentState.and.returnValue({ workspace_owner: "wirecloud", workspace_name: "home", + workspace_title: "Home", view: "workspace" }); @@ -498,6 +503,7 @@ Wirecloud.HistoryManager.getCurrentState.and.returnValue({ workspace_owner: "wirecloud", workspace_name: "home", + workspace_title: "Home", view: "workspace" }); @@ -526,6 +532,7 @@ Wirecloud.HistoryManager.getCurrentState.and.returnValue({ workspace_owner: "wirecloud", workspace_name: "home", + workspace_title: "Home", view: "workspace" }); Wirecloud.ContextManager.and.throwError("invalid data"); @@ -553,6 +560,7 @@ Wirecloud.HistoryManager.getCurrentState.and.returnValue({ workspace_owner: "wirecloud", workspace_name: "home", + workspace_title: "Home", view: "workspace" }); @@ -626,7 +634,7 @@ }); }); - var task = Wirecloud.loadWorkspace({owner: "wirecloud", name: "home"}); + var task = Wirecloud.loadWorkspace({owner: "wirecloud", name: "home", title: "Home"}); expect(task).toEqual(jasmine.any(Wirecloud.Task)); task.then(function (workspace) { @@ -954,7 +962,8 @@ var workspace = { id: 1, owner: "wirecloud", - name: "home" + name: "home", + title: "Home" }; Wirecloud.workspaceInstances = { 1: workspace @@ -974,7 +983,7 @@ }); }); - var task = Wirecloud.removeWorkspace({owner: "wirecloud", name: "home"}); + var task = Wirecloud.removeWorkspace({owner: "wirecloud", name: "home", title: "Home"}); expect(task).toEqual(jasmine.any(Wirecloud.Task)); task.then(function () { @@ -988,7 +997,8 @@ var workspace = { id: 100, owner: "wirecloud", - name: "home" + name: "home", + title: "Home" }; Wirecloud.workspaceInstances = { 100: workspace @@ -1022,7 +1032,8 @@ var workspace = { id: 100, owner: "wirecloud", - name: "home" + name: "home", + title: "Home" }; Wirecloud.workspaceInstances = { 100: workspace diff --git a/src/js_tests/wirecloud/ui/SharingWindowMenuSpec.js b/src/js_tests/wirecloud/ui/SharingWindowMenuSpec.js index 6ffcfd9fa6..2a14613a72 100644 --- a/src/js_tests/wirecloud/ui/SharingWindowMenuSpec.js +++ b/src/js_tests/wirecloud/ui/SharingWindowMenuSpec.js @@ -44,6 +44,11 @@ }; }); + afterAll(() => { + // TODO + Wirecloud.contextManager = null; + }); + describe("new SharingWindowMenu(workspace)", () => { it("requires the workspace parameter", () => { diff --git a/src/js_tests/wirecloud/ui/WorkspaceViewSpec.js b/src/js_tests/wirecloud/ui/WorkspaceViewSpec.js new file mode 100644 index 0000000000..969f3683f6 --- /dev/null +++ b/src/js_tests/wirecloud/ui/WorkspaceViewSpec.js @@ -0,0 +1,1010 @@ +/* + * Copyright (c) 2018 Future Internet Consulting and Development Solutions S.L. + * + * This file is part of Wirecloud Platform. + * + * Wirecloud Platform is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * Wirecloud is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Wirecloud Platform. If not, see + * . + * + */ + +/* globals StyledElements, Wirecloud */ + + +(function (ns, utils) { + + "use strict"; + + const callEventListener = function callEventListener(instance, event) { + var largs = Array.prototype.slice.call(arguments, 2); + largs.unshift(instance); + instance.addEventListener.calls.allArgs().some(function (args) { + if (args[0] === event) { + args[1].apply(instance, largs); + return true; + } + }); + }; + + const create_workspace = function create_workspace(options) { + let workspace = utils.merge({ + id: "1", + owner: "user", + name: "empty", + title: "Dashboard Title", + tabs: [ + { + id: "8", + widgets: [], + name: "tab", + title: "Tab", + initial: true + } + ], + operators: [], + preferences: {}, + createTab: jasmine.createSpy("createTab"), + isAllowed: jasmine.createSpy("isAllowed").and.returnValue(false) + }, options); + + workspace.addEventListener = jasmine.createSpy("addEventListener"); + workspace.removeEventListener = jasmine.createSpy("removeEventListener"); + + return workspace; + }; + + describe("WorkspaceView", () => { + + var sidebar = null; + + beforeAll(() => { + // TODO + Wirecloud.contextManager = { + "get": jasmine.createSpy("get").and.returnValue("currentuser") + }; + + // TODO + Wirecloud.ui.MissingDependenciesWindowMenu = jasmine.createSpy("MissingDependenciesWindowMenu").and.callFake(function () { + this.show = jasmine.createSpy("show"); + }); + }); + + afterAll(() => { + // TODO + Wirecloud.contextManager = null; + }); + + beforeEach(() => { + Wirecloud.clearEventListeners("loaded"); + Wirecloud.contextManager.get.and.returnValue("currentuser"); + + // Init required mocks + ns.WorkspaceListItems = jasmine.createSpy("WorkspaceListItems"); + utils.inherit(ns.WorkspaceListItems, StyledElements.DynamicMenuItems); + + ns.WorkspaceViewMenuItems = jasmine.createSpy("WorkspaceViewMenuItems"); + utils.inherit(ns.WorkspaceViewMenuItems, StyledElements.DynamicMenuItems); + + ns.ComponentSidebar = jasmine.createSpy("ComponentSidebar").and.callFake(function () { + sidebar = this; + this.wrapperElement = document.createElement('div'); + this.addEventListener = jasmine.createSpy("addEventListener"); + }); + utils.inherit(ns.ComponentSidebar, StyledElements.StyledElement); + + ns.WorkspaceTabView = jasmine.createSpy("WorkspaceTabView").and.callFake(function (id, notebook, options) { + StyledElements.Tab.call(this, id, notebook, options); + this.id = "8"; + this.createWidget = jasmine.createSpy("createWidget"); + this.findWidget = jasmine.createSpy("findWidget"); + this.widgets = []; + }); + utils.inherit(ns.WorkspaceTabView, StyledElements.Tab); + + spyOn(Wirecloud, "Workspace").and.callFake(function () { + this.operators = []; + this.operatorsById = {}; + this.findOperator = jasmine.createSpy("findOperator"); + this.addEventListener = jasmine.createSpy("addEventListener"); + this.removeEventListener = jasmine.createSpy("removeEventListener"); + }); + }); + + describe("new WorkspaceView(id, options)", () => { + + it("should work without providing options", () => { + let view = new ns.WorkspaceView(1); + Wirecloud.dispatchEvent('loaded'); + + expect(ns.ComponentSidebar).toHaveBeenCalledWith(); + expect(view.view_name).toBe("workspace"); + }); + + it("should allow to change the activeWorkspace through the workspace menu", () => { + spyOn(Wirecloud.UserInterfaceManager, "monitorTask"); + spyOn(Wirecloud, "changeActiveWorkspace"); + let view = new ns.WorkspaceView(1); + Wirecloud.dispatchEvent('loaded'); + let workspace = create_workspace(); + + ns.WorkspaceListItems.calls.argsFor(0)[0]("notused", workspace); + + expect(view.wsMenu).not.toEqual(null); + expect(Wirecloud.changeActiveWorkspace).toHaveBeenCalledWith(workspace); + }); + + it("should allow to add new widgets", (done) => { + spyOn(Wirecloud.UserInterfaceManager, "monitorTask"); + let view = new ns.WorkspaceView(1); + Wirecloud.dispatchEvent('loaded'); + let workspace = create_workspace(); + view.loadWorkspace(workspace); + view.activeTab.createWidget.and.returnValue(Promise.resolve()); + + let group = { + meta: { + type: "widget" + } + }; + let button = { + disable: jasmine.createSpy("disable"), + enable: jasmine.createSpy("enable") + }; + callEventListener(sidebar, "create", group, button); + expect(view.activeTab.createWidget).toHaveBeenCalledWith(group.meta); + expect(button.disable).toHaveBeenCalledWith(); + + setTimeout(() => { + expect(button.enable).toHaveBeenCalledWith(); + done(); + }, 0); + }); + + it("should handle errors when adding new widgets", (done) => { + spyOn(Wirecloud.UserInterfaceManager, "monitorTask"); + let view = new ns.WorkspaceView(1); + Wirecloud.dispatchEvent('loaded'); + let workspace = create_workspace(); + view.loadWorkspace(workspace); + view.activeTab.createWidget.and.returnValue(Promise.reject()); + + let group = { + meta: { + type: "widget" + } + }; + let button = { + disable: jasmine.createSpy("disable"), + enable: jasmine.createSpy("enable") + }; + callEventListener(sidebar, "create", group, button); + expect(view.activeTab.createWidget).toHaveBeenCalledWith(group.meta); + expect(button.disable).toHaveBeenCalledWith(); + + setTimeout(() => { + expect(button.enable).toHaveBeenCalledWith(); + done(); + }, 0); + }); + + it("should allow to merge mashups", (done) => { + spyOn(Wirecloud.UserInterfaceManager, "monitorTask"); + let view = new ns.WorkspaceView(1); + Wirecloud.dispatchEvent('loaded'); + let workspace = create_workspace(); + view.loadWorkspace(workspace); + spyOn(Wirecloud, "changeActiveWorkspace"); + spyOn(Wirecloud, "mergeWorkspace").and.returnValue(Promise.resolve(workspace)); + + let group = { + meta: { + type: "mashup" + } + }; + let button = { + disable: jasmine.createSpy("disable"), + enable: jasmine.createSpy("enable") + }; + callEventListener(sidebar, "create", group, button); + expect(button.disable).toHaveBeenCalledWith(); + + setTimeout(() => { + expect(Wirecloud.changeActiveWorkspace).toHaveBeenCalledWith(workspace, {history: "replace"}); + done(); + }); + }); + + it("should handle missing components error when merging mashups", (done) => { + spyOn(Wirecloud.UserInterfaceManager, "monitorTask"); + let view = new ns.WorkspaceView(1); + Wirecloud.dispatchEvent('loaded'); + let workspace = create_workspace(); + view.loadWorkspace(workspace); + spyOn(Wirecloud, "changeActiveWorkspace"); + let error_details = { + missingDependencies: [] + }; + spyOn(Wirecloud, "mergeWorkspace").and.returnValue(Promise.reject({ + details: error_details + })); + + let group = { + meta: { + type: "mashup" + } + }; + let button = { + disable: jasmine.createSpy("disable"), + enable: jasmine.createSpy("enable") + }; + callEventListener(sidebar, "create", group, button); + expect(button.disable).toHaveBeenCalledWith(); + + setTimeout(() => { + expect(Wirecloud.ui.MissingDependenciesWindowMenu).toHaveBeenCalledWith(null, error_details); + expect(button.enable).toHaveBeenCalledWith(); + expect(Wirecloud.changeActiveWorkspace).not.toHaveBeenCalled(); + done(); + }); + }); + + it("should handle error when merging mashups", (done) => { + spyOn(Wirecloud.UserInterfaceManager, "monitorTask"); + let view = new ns.WorkspaceView(1); + Wirecloud.dispatchEvent('loaded'); + let workspace = create_workspace(); + view.loadWorkspace(workspace); + spyOn(Wirecloud, "changeActiveWorkspace"); + spyOn(Wirecloud, "mergeWorkspace").and.returnValue(Promise.reject({})); + + let group = { + meta: { + type: "mashup" + } + }; + let button = { + disable: jasmine.createSpy("disable"), + enable: jasmine.createSpy("enable") + }; + callEventListener(sidebar, "create", group, button); + expect(button.disable).toHaveBeenCalledWith(); + + setTimeout(() => { + expect(button.enable).toHaveBeenCalledWith(); + expect(Wirecloud.changeActiveWorkspace).not.toHaveBeenCalled(); + done(); + }); + }); + + }); + + describe("buildStateData()", () => { + + it("should return a valid state", () => { + let view = new ns.WorkspaceView(1); + + expect(view.buildStateData()).toEqual(jasmine.any(Object)); + }); + + }); + + describe("canGoUp()", () => { + + it("should return true if current loaded workspace is not wirecloud/home", () => { + Wirecloud.activeWorkspace = { + owner: "oneuser", + name: "dashboard" + }; + let view = new ns.WorkspaceView(1); + + expect(view.canGoUp()).toBe(true); + }); + + it("should return false if there is any workspace loaded", () => { + Wirecloud.activeWorkspace = null; + let view = new ns.WorkspaceView(1); + + expect(view.canGoUp()).toBe(false); + }); + + it("should return false if the current loaded workspace is wirecloud/home", () => { + Wirecloud.activeWorkspace = { + owner: "wirecloud", + name: "home" + }; + let view = new ns.WorkspaceView(1); + + expect(view.canGoUp()).toBe(false); + }); + + }); + + describe("drawAttention(id)", () => { + + var view; + + beforeEach(() => { + view = new ns.WorkspaceView(1); + Wirecloud.dispatchEvent('loaded'); + let workspace = create_workspace(); + view.loadWorkspace(workspace); + }); + + it("should do nothing if the widget is not present", () => { + expect(view.drawAttention("1")).toBe(view); + }); + + it("should highlight the widget if present", () => { + let widget = { + raiseToTop: jasmine.createSpy("raiseToTop"), + highlight: jasmine.createSpy("highlight"), + tab: { + highlight: jasmine.createSpy("highlight") + } + }; + widget.raiseToTop.and.returnValue(widget); + widget.highlight.and.returnValue(widget); + view.tabs[0].findWidget.and.returnValue(widget); + + expect(view.drawAttention("14")).toBe(view); + }); + + }); + + describe("buildAddWidgetButton()", () => { + + var view; + + beforeEach(() => { + view = new ns.WorkspaceView(1); + }); + + it("should slide in the component panel", () => { + view.showcase = { + searchComponents: { + refresh: jasmine.createSpy("refresh") + } + }; + spyOn(view.layout, "slideIn"); + let button = view.buildAddWidgetButton(); + + button.click(); + + expect(view.showcase.searchComponents.refresh).toHaveBeenCalledWith(); + expect(view.layout.slideIn).toHaveBeenCalledWith(); + }); + + it("should slideOut the component panel", () => { + spyOn(view.layout, "slideOut"); + let button = view.buildAddWidgetButton(); + + button.active = true; + button.click(); + + expect(view.layout.slideOut).toHaveBeenCalledWith(); + }); + }); + + describe("findTab(id)", () => { + + var view; + + beforeEach(() => { + view = new ns.WorkspaceView(1); + Wirecloud.dispatchEvent('loaded'); + let workspace = create_workspace(); + view.loadWorkspace(workspace); + }); + + it("should return null if the tab is not present", () => { + expect(view.findTab("1")).toBe(null); + }); + + it("should return a tab if present", () => { + expect(view.findTab("8")).not.toBe(null); + }); + + }); + + describe("findWidget(id)", () => { + + var view; + + beforeEach(() => { + view = new ns.WorkspaceView(1); + Wirecloud.dispatchEvent('loaded'); + let workspace = create_workspace(); + view.loadWorkspace(workspace); + }); + + it("should return null if the widget is not present", () => { + expect(view.findWidget("1")).toBe(null); + }); + + it("should return a widget if present", () => { + let widget = {}; + view.tabs[0].findWidget.and.returnValue(widget); + + expect(view.findWidget("8")).toBe(widget); + }); + + }); + + describe("getBreadcrumb()", () => { + + it("should return loading... if WireCloud is still loading", () => { + spyOn(Wirecloud.HistoryManager, "getCurrentState").and.returnValue({}); + let view = new ns.WorkspaceView(1); + + expect(view.getBreadcrumb()).toEqual([{ + 'label': 'loading...' + }]); + }); + + it("should use metadata from navigation history if available", () => { + spyOn(Wirecloud.HistoryManager, "getCurrentState").and.returnValue({ + "workspace_owner": "otheruser", + "workspace_title": "dashboard" + }); + let view = new ns.WorkspaceView(1); + + expect(view.getBreadcrumb()).toEqual([ + { + 'label': 'otheruser' + }, + { + 'label': 'dashboard' + } + ]); + }); + + it("should return a breadcum based on the loaded workspace", () => { + spyOn(Wirecloud.HistoryManager, "getCurrentState").and.returnValue({}); + let view = new ns.WorkspaceView(1); + let workspace = create_workspace(); + view.loadWorkspace(workspace); + + expect(view.getBreadcrumb()).toEqual([ + { + 'label': 'user' + }, + { + 'label': 'Dashboard Title' + } + ]); + }); + + }); + + describe("getTitle()", () => { + + it("should return loading... if WireCloud is still loading", () => { + spyOn(Wirecloud.HistoryManager, "getCurrentState").and.returnValue({}); + let view = new ns.WorkspaceView(1); + + expect(view.getTitle()).toEqual('loading...'); + }); + + it("should use metadata from navigation history if available", () => { + spyOn(Wirecloud.HistoryManager, "getCurrentState").and.returnValue({ + "workspace_owner": "otheruser", + "workspace_title": "dashboard" + }); + let view = new ns.WorkspaceView(1); + + expect(view.getTitle()).toEqual('otheruser/dashboard'); + }); + + it("should return a breadcum based on the loaded workspace", () => { + spyOn(Wirecloud.HistoryManager, "getCurrentState").and.returnValue({}); + let view = new ns.WorkspaceView(1); + let workspace = create_workspace(); + view.loadWorkspace(workspace); + + expect(view.getTitle()).toEqual('user/Dashboard Title'); + }); + + }); + + describe("getToolbarButtons()", () => { + + it("should return an empty list if user is anonymous", () => { + Wirecloud.contextManager.get.and.returnValue("anonymous"); + spyOn(Wirecloud.HistoryManager, "getCurrentState").and.returnValue({}); + let view = new ns.WorkspaceView(1); + + expect(view.getToolbarButtons()).toEqual([]); + }); + + it("should return a button list if user is not anonymous", () => { + spyOn(Wirecloud.HistoryManager, "getCurrentState").and.returnValue({}); + let view = new ns.WorkspaceView(1); + + expect(view.getToolbarButtons()).toEqual([ + jasmine.any(StyledElements.Button), + jasmine.any(StyledElements.Button), + jasmine.any(StyledElements.Button), + jasmine.any(StyledElements.Button) + ]); + }); + + it("should return a button list if user is not anonymous and there is a loaded workspace", () => { + spyOn(Wirecloud.HistoryManager, "getCurrentState").and.returnValue({}); + spyOn(Wirecloud.UserInterfaceManager, "changeCurrentView"); + let view = new ns.WorkspaceView(1); + let workspace = create_workspace(); + workspace.isAllowed.and.returnValue(true); + view.loadWorkspace(workspace); + + let buttons = view.getToolbarButtons(); + + expect(buttons).toEqual([ + jasmine.any(StyledElements.Button), + jasmine.any(StyledElements.Button), + jasmine.any(StyledElements.Button), + jasmine.any(StyledElements.Button) + ]); + + buttons[1].click(); + buttons[2].click(); + buttons[3].click(); + + expect(Wirecloud.UserInterfaceManager.changeCurrentView).toHaveBeenCalledWith("wiring"); + expect(Wirecloud.UserInterfaceManager.changeCurrentView).toHaveBeenCalledWith("myresources"); + expect(Wirecloud.UserInterfaceManager.changeCurrentView).toHaveBeenCalledWith("marketplace"); + expect(Wirecloud.UserInterfaceManager.changeCurrentView.calls.count()).toBe(3); + + }); + + }); + + describe("getToolbarMenu()", () => { + + it("should return null while not loaded", () => { + spyOn(Wirecloud.HistoryManager, "getCurrentState").and.returnValue({}); + let view = new ns.WorkspaceView(1); + let workspace = create_workspace(); + view.loadWorkspace(workspace); + + expect(view.getToolbarMenu()).toBe(null); + }); + + it("should return the toolbar menu once loaded", () => { + spyOn(Wirecloud.HistoryManager, "getCurrentState").and.returnValue({ + "workspace_owner": "otheruser", + "workspace_title": "dashboard" + }); + let view = new ns.WorkspaceView(1); + let workspace = create_workspace(); + view.loadWorkspace(workspace); + + expect(view.getToolbarMenu()).toBe(view.wsMenu); + }); + + it("should return null if the current user is anonymous", () => { + Wirecloud.contextManager.get.and.returnValue("anonymous"); + spyOn(Wirecloud.HistoryManager, "getCurrentState").and.returnValue({ + "workspace_owner": "otheruser", + "workspace_title": "dashboard" + }); + let view = new ns.WorkspaceView(1); + let workspace = create_workspace(); + view.loadWorkspace(workspace); + + expect(view.getToolbarMenu()).toBe(null); + }); + + }); + + describe("goUp()", () => { + + it("changes the active workspace", () => { + spyOn(Wirecloud, "changeActiveWorkspace"); + let view = new ns.WorkspaceView(1); + + expect(view.goUp()).toBe(view); + + expect(Wirecloud.changeActiveWorkspace).toHaveBeenCalledWith({ + owner: 'wirecloud', + name: 'home' + }); + }); + + }); + + describe("loadWorkspace(workspace[, options])", () => { + + var view; + + beforeEach(() => { + view = new ns.WorkspaceView(1); + Wirecloud.dispatchEvent('loaded'); + }); + + it("should load empty workspaces", () => { + let workspace = create_workspace(); + view.loadWorkspace(workspace); + + expect(view.tabs).toEqual([jasmine.any(StyledElements.Tab)]); + expect(view.activeTab).toBe(view.tabs[0]); + expect(view.name).toEqual(workspace.name); + expect(view.title).toEqual(workspace.title); + expect(view.widgets).toEqual([]); + + // Normal visualization should provide a power by wirecloud button + expect(view.seeOnWirecloudButton).toEqual(undefined); + expect(view.poweredByWirecloudButton).toEqual(jasmine.any(StyledElements.Button)); + spyOn(window, "open"); + view.poweredByWirecloudButton.click(); + + expect(window.open).toHaveBeenCalledWith(jasmine.anything(), "_blank"); + }); + + it("should load workspaces with operators", () => { + spyOn(view.layout.content, "appendChild"); + let operator = {wrapperElement: {}}; + let workspace = create_workspace({ + operators: [operator] + }); + view.loadWorkspace(workspace); + + expect(view.layout.content.appendChild).toHaveBeenCalledWith(operator.wrapperElement); + }); + + it("should load workspaces using the initialTab option", () => { + let workspace = create_workspace({ + tabs: [ + { + id: "8", + widgets: [], + name: "tab", + title: "Tab", + initial: true + }, + { + id: "12", + widgets: [], + name: "tab2", + title: "Tab 2", + initial: false + } + ] + }); + view.loadWorkspace(workspace, {initialtab: "tab2"}); + + expect(view.tabs).toEqual([ + jasmine.any(ns.WorkspaceTabView), + jasmine.any(ns.WorkspaceTabView) + ]); + expect(view.activeTab).toBe(view.tabs[1]); + expect(view.name).toEqual(workspace.name); + expect(view.title).toEqual(workspace.title); + }); + + it("should load editable workspaces", (done) => { + let view = new ns.WorkspaceView(1); + let workspace = create_workspace(); + workspace.isAllowed.and.returnValue(true); + let real_notebook = StyledElements.Notebook; + spyOn(StyledElements, "Notebook").and.callFake(function () { + real_notebook.call(this); + this.addButton = jasmine.createSpy("addButton"); + spyOn(this, "createTab").and.callThrough(); + }); + utils.inherit(StyledElements.Notebook, real_notebook); + view.loadWorkspace(workspace); + + // Check the interface provides a button for creating tabs + let tab = {}; + workspace.createTab.and.returnValue(Promise.resolve(tab)); + + let button = view.notebook.addButton.calls.argsFor(0)[0]; + spyOn(button, "disable"); + spyOn(button, "enable"); + button.click(); + expect(button.disable).toHaveBeenCalledWith(); + + setTimeout(() => { + expect(workspace.createTab).toHaveBeenCalledWith(); + expect(view.notebook.createTab).toHaveBeenCalledWith({ + tab_constructor: Wirecloud.ui.WorkspaceTabView, + model: tab, + workspace: view + }); + expect(button.enable).toHaveBeenCalledWith(); + done(); + }, 0); + }); + + it("should handle errors creating tabs", (done) => { + let view = new ns.WorkspaceView(1); + let workspace = create_workspace(); + workspace.isAllowed.and.returnValue(true); + let real_notebook = StyledElements.Notebook; + spyOn(StyledElements, "Notebook").and.callFake(function () { + real_notebook.call(this); + this.addButton = jasmine.createSpy("addButton"); + spyOn(this, "createTab").and.callThrough(); + }); + utils.inherit(StyledElements.Notebook, real_notebook); + view.loadWorkspace(workspace); + workspace.createTab.and.returnValue(Promise.reject()); + + let button = view.notebook.addButton.calls.argsFor(0)[0]; + spyOn(button, "disable"); + spyOn(button, "enable"); + view.notebook.createTab.calls.reset(); + button.click(); + expect(button.disable).toHaveBeenCalledWith(); + + setTimeout(() => { + expect(workspace.createTab).toHaveBeenCalledWith(); + expect(view.notebook.createTab).not.toHaveBeenCalled(); + expect(button.enable).toHaveBeenCalledWith(); + done(); + }, 0); + }); + + it("should load workspaces when supporting the fullscreen mode", () => { + let workspace = create_workspace(); + spyOn(Wirecloud.Utils, "isFullscreenSupported").and.returnValue(true); + spyOn(Wirecloud.Utils, "onFullscreenChange"); + + view.loadWorkspace(workspace); + + expect(Wirecloud.Utils.onFullscreenChange).toHaveBeenCalledWith(view.notebook, jasmine.any(Function)); + + Wirecloud.Utils.onFullscreenChange.calls.argsFor(0)[1](); + + view.notebook = { + fullscreen: true, + addClassName: jasmine.createSpy("addClassName"), + exitFullscreen: jasmine.createSpy("exitFullscreen"), + requestFullscreen: jasmine.createSpy("requestFullscreen") + }; + + Wirecloud.Utils.onFullscreenChange.calls.argsFor(0)[1](); + + view.fullscreenButton.click(); + expect(view.notebook.exitFullscreen).toHaveBeenCalledWith(); + + view.notebook.fullscreen = false; + view.fullscreenButton.click(); + expect(view.notebook.requestFullscreen).toHaveBeenCalledWith(); + }); + + it("should load workspaces on embedded mode", () => { + let workspace = create_workspace(); + spyOn(Wirecloud.Utils, "isFullscreenSupported").and.returnValue(true); + spyOn(Wirecloud.Utils, "onFullscreenChange"); + Wirecloud.contextManager.get.and.returnValue("embedded"); + + view.loadWorkspace(workspace); + + // embedded visualizations should provide a see on wirecloud button + expect(view.poweredByWirecloudButton).toEqual(undefined); + expect(view.seeOnWirecloudButton).toEqual(jasmine.any(StyledElements.Button)); + spyOn(window, "open"); + view.seeOnWirecloudButton.click(); + + expect(window.open).toHaveBeenCalledWith(jasmine.anything(), "_blank"); + }); + + it("should handle operator create events", () => { + spyOn(Wirecloud.HistoryManager, "replaceState"); + Wirecloud.UserInterfaceManager.header = { + "refresh": jasmine.createSpy("refresh") + }; + let workspace = create_workspace(); + view.loadWorkspace(workspace); + spyOn(view.layout.content, "appendChild"); + let operator = {wrapperElement: {}}; + + callEventListener(workspace, "createoperator", operator); + + expect(view.layout.content.appendChild).toHaveBeenCalledWith(operator.wrapperElement); + }); + + it("should handle operator remove events", () => { + spyOn(Wirecloud.HistoryManager, "replaceState"); + Wirecloud.UserInterfaceManager.header = { + "refresh": jasmine.createSpy("refresh") + }; + let workspace = create_workspace(); + view.loadWorkspace(workspace); + spyOn(view.layout.content, "removeChild"); + let operator = {wrapperElement: {}}; + + callEventListener(workspace, "removeoperator", operator); + + expect(view.layout.content.removeChild).toHaveBeenCalledWith(operator.wrapperElement); + }); + + it("should handle workspace changes", () => { + spyOn(Wirecloud.HistoryManager, "replaceState"); + Wirecloud.UserInterfaceManager.header = { + "refresh": jasmine.createSpy("refresh") + }; + let workspace = create_workspace(); + view.loadWorkspace(workspace); + + callEventListener(workspace, "change"); + + expect(Wirecloud.HistoryManager.replaceState).toHaveBeenCalled(); + }); + + it("should handle workspace remove events", () => { + spyOn(Wirecloud.UserInterfaceManager, "monitorTask"); + spyOn(Wirecloud, "changeActiveWorkspace"); + let workspace = create_workspace(); + view.loadWorkspace(workspace); + + callEventListener(workspace, "remove"); + + expect(Wirecloud.changeActiveWorkspace).toHaveBeenCalledWith({ + owner: 'wirecloud', + name: 'home' + }); + }); + + it("should handle workspace unload events", () => { + let workspace = create_workspace(); + view.loadWorkspace(workspace); + + callEventListener(workspace, "unload"); + + expect(view.model).toBe(null); + }); + + }); + + describe("onHistoryChange(newState)", () => { + + var view; + + beforeEach(() => { + view = new ns.WorkspaceView(1); + Wirecloud.dispatchEvent('loaded'); + Wirecloud.activeWorkspace = null; + spyOn(Wirecloud, "changeActiveWorkspace"); + }); + + it("should handle changes to deleted workspaces (no active workspace)", () => { + spyOn(Wirecloud, "dispatchEvent"); + + view.onHistoryChange({ + workspace_owner: "user", + workspace_name: "dashboard" + }); + + expect(Wirecloud.dispatchEvent).toHaveBeenCalledWith('viewcontextchanged'); + }); + + it("should handle changes to deleted workspaces", () => { + let unload = jasmine.createSpy("unload"); + Wirecloud.activeWorkspace = { + unload: unload + }; + spyOn(Wirecloud, "dispatchEvent"); + + view.onHistoryChange({ + workspace_owner: "user", + workspace_name: "dashboard" + }); + + expect(Wirecloud.dispatchEvent).toHaveBeenCalledWith('viewcontextchanged'); + expect(unload).toHaveBeenCalledWith(); + }); + + it("should handle workspace changes", () => { + let workspace = create_workspace(); + Wirecloud.workspacesByUserAndName = { + "user": { + "dashboard": workspace + } + }; + + view.onHistoryChange({ + workspace_owner: "user", + workspace_name: "dashboard" + }); + + expect(Wirecloud.changeActiveWorkspace).toHaveBeenCalledWith(workspace, {initialtab: undefined, history: 'ignore'}); + }); + + it("should handle tab changes", () => { + let workspace = create_workspace({ + tabs: [ + { + id: "8", + widgets: [], + name: "tab", + title: "Tab", + initial: true + }, + { + id: "12", + widgets: [], + name: "tab2", + title: "Tab 2", + initial: false + } + ] + }); + view.loadWorkspace(workspace); + Wirecloud.activeWorkspace = workspace; + Wirecloud.workspacesByUserAndName = { + "user": { + "dashboard": workspace + } + }; + spyOn(view, "findTab").and.returnValue(view.tabs[1]); + spyOn(view.notebook, "goToTab"); + + view.onHistoryChange({ + workspace_owner: "user", + workspace_name: "dashboard", + tab: "tab2" + }); + + expect(view.notebook.goToTab).toHaveBeenCalledWith(view.tabs[1]); + expect(Wirecloud.changeActiveWorkspace).not.toHaveBeenCalled(); + }); + + it("should handle no changes", () => { + let workspace = create_workspace(); + view.loadWorkspace(workspace); + Wirecloud.activeWorkspace = workspace; + Wirecloud.workspacesByUserAndName = { + "user": { + "dashboard": workspace + } + }; + spyOn(view.notebook, "goToTab"); + + view.onHistoryChange({ + workspace_owner: "user", + workspace_name: "dashboard" + }); + + expect(view.notebook.goToTab).not.toHaveBeenCalled(); + expect(Wirecloud.changeActiveWorkspace).not.toHaveBeenCalled(); + }); + + }); + + describe("showSettings()", () => { + + it("should display the preferences window menu", () => { + let view = new ns.WorkspaceView(1); + Wirecloud.dispatchEvent('loaded'); + let workspace = create_workspace(); + view.loadWorkspace(workspace); + let show = jasmine.createSpy("show"); + // TODO + Wirecloud.ui.PreferencesWindowMenu = jasmine.createSpy("PreferencesWindowMenu").and.callFake(function () { + this.show = show + }); + + expect(view.showSettings()).toBe(view); + + expect(Wirecloud.ui.PreferencesWindowMenu).toHaveBeenCalledWith("workspace", workspace.preferences); + expect(show).toHaveBeenCalledWith(); + }); + + }); + + }); + +})(Wirecloud.ui, StyledElements.Utils); diff --git a/src/wirecloud/platform/static/js/wirecloud/Workspace.js b/src/wirecloud/platform/static/js/wirecloud/Workspace.js index c0ca6b50b7..69f57248a6 100644 --- a/src/wirecloud/platform/static/js/wirecloud/Workspace.js +++ b/src/wirecloud/platform/static/js/wirecloud/Workspace.js @@ -455,7 +455,7 @@ */ remove: function remove() { return Wirecloud.removeWorkspace(this).then(() => { - this.dispatchEvent('remove'); + this.dispatchEvent('remove').unload(); }); }, diff --git a/src/wirecloud/platform/static/js/wirecloud/core.js b/src/wirecloud/platform/static/js/wirecloud/core.js index 400ba97027..9bfa7c87a2 100644 --- a/src/wirecloud/platform/static/js/wirecloud/core.js +++ b/src/wirecloud/platform/static/js/wirecloud/core.js @@ -47,6 +47,7 @@ }); Object.freeze(Wirecloud.events); Wirecloud.addEventListener = StyledElements.ObjectWithEvents.prototype.addEventListener; + Wirecloud.clearEventListeners = StyledElements.ObjectWithEvents.prototype.clearEventListeners; Wirecloud.dispatchEvent = StyledElements.ObjectWithEvents.prototype.dispatchEvent; var onCreateWorkspaceSuccess = function onCreateWorkspaceSuccess(response) { @@ -323,6 +324,7 @@ state = { workspace_owner: workspace.owner, workspace_name: workspace.name, + workspace_title: workspace.title, view: "workspace" }; if (options.initialtab != null) { diff --git a/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceListItems.js b/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceListItems.js index bb07ed6012..6b1ac9de73 100644 --- a/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceListItems.js +++ b/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceListItems.js @@ -31,7 +31,7 @@ this.handler = handler; }; - WorkspaceListItems.prototype = new StyledElements.DynamicMenuItems(); + utils.inherit(WorkspaceListItems, StyledElements.DynamicMenuItems); WorkspaceListItems.prototype.build = function () { var workspace_name, items, workspace, username, user_workspaces; diff --git a/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceTabView.js b/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceTabView.js index ade4a47496..a1dad64534 100644 --- a/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceTabView.js +++ b/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceTabView.js @@ -208,6 +208,13 @@ }.bind(this)); }, + /** + * Highlights this tab + */ + highlight: function () { + this.tabElement.classList.add("highlight"); + }, + /** * @param {String} id * @@ -274,6 +281,10 @@ showSettings: function showSettings() { (new Wirecloud.ui.PreferencesWindowMenu('tab', this.model.preferences)).show(); return this; + }, + + unhighlight: function unhighlight() { + this.tabElement.classList.remove("highlight"); } }); diff --git a/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceView.js b/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceView.js index e95c276a1a..23c0b6c677 100644 --- a/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceView.js +++ b/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceView.js @@ -101,18 +101,25 @@ this.on_workspace_change_bound = on_workspace_change.bind(this); this.on_workspace_remove_bound = on_workspace_remove.bind(this); this.on_workspace_unload_bound = on_workspace_unload.bind(this); + this.on_workspace_createoperator_bound = on_workspace_createoperator.bind(this); + this.on_workspace_removeoperator_bound = on_workspace_removeoperator.bind(this); - Wirecloud.addEventListener('loaded', function () { + Wirecloud.addEventListener('loaded', () => { this.showcase = new Wirecloud.ui.ComponentSidebar(); this.layout.appendChild(this.showcase); - this.showcase.addEventListener('create', function (showcase, group, button) { + this.showcase.addEventListener('create', (showcase, group, button) => { button.disable(); if (group.meta.type === 'widget') { - this.activeTab.createWidget(group.meta).then(function () { - button.enable(); - }); + this.activeTab.createWidget(group.meta).then( + () => { + button.enable(); + }, + () => { + button.enable(); + } + ); } else { Wirecloud.UserInterfaceManager.monitorTask( Wirecloud.mergeWorkspace( @@ -120,23 +127,26 @@ { mashup: group.meta.uri } - ).then((workspace) => { - return Wirecloud.changeActiveWorkspace(workspace); - }, (error) => { - button.enable(); - var dialog; - if (error.details != null && 'missingDependencies' in error.details) { - // Show missing dependencies - dialog = new Wirecloud.ui.MissingDependenciesWindowMenu(null, error.details); - } else { - dialog = new Wirecloud.ui.MessageWindowMenu(error, Wirecloud.constants.LOGGING.ERROR_MSG); + ).then( + (workspace) => { + return Wirecloud.changeActiveWorkspace(workspace, {history: "replace"}); + }, + (error) => { + button.enable(); + var dialog; + if (error.details != null && 'missingDependencies' in error.details) { + // Show missing dependencies + dialog = new Wirecloud.ui.MissingDependenciesWindowMenu(null, error.details); + } else { + dialog = new Wirecloud.ui.MessageWindowMenu(error, Wirecloud.constants.LOGGING.ERROR_MSG); + } + dialog.show(); } - dialog.show(); - }) + ) ); } - }.bind(this)); - }.bind(this)); + }); + }); }; utils.inherit(WorkspaceView, StyledElements.Alternative); @@ -195,10 +205,12 @@ this.model.addEventListener('unload', this.on_workspace_unload_bound); this.model.addEventListener('change', this.on_workspace_change_bound); this.model.addEventListener('remove', this.on_workspace_remove_bound); + this.model.addEventListener('createoperator', this.on_workspace_createoperator_bound); + this.model.addEventListener('removeoperator', this.on_workspace_removeoperator_bound); - this.model.operators.forEach(function (operator) { + this.model.operators.forEach((operator) => { this.layout.content.appendChild(operator.wrapperElement); - }, this); + }); var initialTab = null; var requestedTab = null; @@ -243,7 +255,7 @@ title: utils.gettext('Full screen') }); this.notebook.addButton(this.fullscreenButton); - Wirecloud.Utils.onFullscreenChange(this.notebook, function () { + Wirecloud.Utils.onFullscreenChange(this.notebook, () => { this.fullscreenButton.removeIconClassName(['fa-expand', 'fa-compress']); if (this.notebook.fullscreen) { this.fullscreenButton.addIconClassName('fa-compress'); @@ -254,14 +266,14 @@ this.fullscreenButton.setTitle(utils.gettext('Full screen')); this.notebook.removeClassName('fullscreen'); } - }.bind(this)); - this.fullscreenButton.addEventListener('click', function () { + }); + this.fullscreenButton.addEventListener('click', () => { if (this.notebook.fullscreen) { this.notebook.exitFullscreen(); } else { this.notebook.requestFullscreen(); } - }.bind(this)); + }); } if (Wirecloud.contextManager.get('mode') === 'embedded') { @@ -269,24 +281,19 @@ 'class': 'powered-by-wirecloud' }); this.notebook.addButton(this.seeOnWirecloudButton); - this.seeOnWirecloudButton.addEventListener('click', function () { + this.seeOnWirecloudButton.addEventListener('click', () => { var url = Wirecloud.URLs.WORKSPACE_VIEW.evaluate({owner: encodeURIComponent(this.model.owner), name: encodeURIComponent(this.model.name)}); window.open(url, '_blank'); - }.bind(this)); + }); } else { this.poweredByWirecloudButton = new StyledElements.Button({ 'class': 'powered-by-wirecloud' }); this.notebook.addButton(this.poweredByWirecloudButton); - this.poweredByWirecloudButton.addEventListener('click', function () {window.open('http://conwet.fi.upm.es/wirecloud/', '_blank');}); + this.poweredByWirecloudButton.addEventListener('click', () => { + window.open('http://conwet.fi.upm.es/wirecloud/', '_blank'); + }); } - - this.model.addEventListener('createoperator', function (workspace_model, operator) { - this.layout.content.appendChild(operator.wrapperElement); - }.bind(this)); - this.model.addEventListener('removeoperator', function (workspace_model, operator) { - this.layout.content.removeChild(operator.wrapperElement); - }.bind(this)); }; WorkspaceView.prototype.buildAddWidgetButton = function buildAddWidgetButton() { @@ -296,14 +303,14 @@ iconClass: "fa fa-archive", stackedIconClass: "fa fa-plus-circle" }); - button.addEventListener('click', function (button) { + button.addEventListener('click', (button) => { if (button.active) { this.showcase.searchComponents.refresh(); this.layout.slideIn(); } else { this.layout.slideOut(); } - }.bind(this)); + }); return button; }; @@ -323,6 +330,7 @@ WorkspaceView.prototype.goUp = function goUp() { Wirecloud.changeActiveWorkspace({owner: 'wirecloud', name: 'home'}); + return this; }; WorkspaceView.prototype.getBreadcrumb = function getBreadcrumb() { @@ -330,12 +338,12 @@ current_state = Wirecloud.HistoryManager.getCurrentState(); - if (Wirecloud.activeWorkspace != null) { + if (this.model != null) { entries = [ { - 'label': Wirecloud.activeWorkspace.owner + 'label': this.model.owner }, { - 'label': Wirecloud.activeWorkspace.title, + 'label': this.model.title, } ]; } else if ('workspace_owner' in current_state) { @@ -357,10 +365,10 @@ WorkspaceView.prototype.getTitle = function getTitle() { var current_state = Wirecloud.HistoryManager.getCurrentState(); - if (Wirecloud.activeWorkspace != null) { - return Wirecloud.activeWorkspace.owner + '/' + Wirecloud.activeWorkspace.name; + if (this.model != null) { + return this.model.owner + '/' + this.model.title; } else if ('workspace_owner' in current_state) { - return current_state.workspace_owner + '/' + current_state.workspace_name; + return current_state.workspace_owner + '/' + current_state.workspace_title; } else { return utils.gettext('loading...'); } @@ -380,8 +388,8 @@ WorkspaceView.prototype.getToolbarButtons = function getToolbarButtons() { if (Wirecloud.contextManager && Wirecloud.contextManager.get('username') !== 'anonymous') { - this.walletButton.setDisabled(Wirecloud.activeWorkspace == null || !Wirecloud.activeWorkspace.isAllowed('edit')); - this.wiringButton.setDisabled(Wirecloud.activeWorkspace == null || !Wirecloud.activeWorkspace.isAllowed('edit')); + this.walletButton.enabled = this.model != null && this.model.isAllowed('edit'); + this.wiringButton.enabled = this.model != null && this.model.isAllowed('edit'); return [this.walletButton, this.wiringButton, this.myresourcesButton, this.marketButton]; } else { return []; @@ -391,7 +399,10 @@ WorkspaceView.prototype.onHistoryChange = function onHistoryChange(newState) { var target_tab, nextWorkspace, alert_msg; - nextWorkspace = Wirecloud.workspacesByUserAndName[newState.workspace_owner][newState.workspace_name]; + if (newState.workspace_owner in Wirecloud.workspacesByUserAndName) { + nextWorkspace = Wirecloud.workspacesByUserAndName[newState.workspace_owner][newState.workspace_name]; + } + if (nextWorkspace == null) { if (Wirecloud.activeWorkspace != null) { Wirecloud.activeWorkspace.unload(); @@ -406,7 +417,7 @@ } else if (Wirecloud.activeWorkspace == null || (nextWorkspace.id !== Wirecloud.activeWorkspace.id)) { Wirecloud.changeActiveWorkspace(nextWorkspace, {initialtab: newState.tab, history: 'ignore'}); } else if (newState.tab != null) { - target_tab = get_tab_by_id.call(this, newState.tab_id); + target_tab = this.findTab(newState.tab_id); this.notebook.goToTab(target_tab); document.title = newState.workspace_owner + '/' + newState.workspace_name; } else { @@ -414,49 +425,20 @@ } }; - WorkspaceView.prototype.rename = function rename(name) { - return this.model.rename(name); - }; - /** - * Removes the workspace currently loaded by this WorkspaceView from the WireCloud server. + * Draw attention from the user to the indicated widget. This method should + * be used when a widget requires intervention from the user. * - * @returns {Wirecloud.Task} - */ - WorkspaceView.prototype.remove = function remove() { - return this.model.remove(); - }; - - WorkspaceView.prototype.publish = function publish(data) { - return this.model.publish(data); - }; - + * @returns {Wirecloud.ui.WorkspaceView} + **/ WorkspaceView.prototype.drawAttention = function drawAttention(id) { var widget = this.findWidget(id); if (widget !== null) { - this.highlightTab(widget.tab); - widget.tab.dragboard.raiseToTop(widget); - widget.highlight(); + widget.raiseToTop().highlight().tab.highlight(); } - }; - WorkspaceView.prototype.highlightTab = function highlightTab(tab) { - - if (typeof tab === 'string') { - tab = this.findTab(tab); - } - - tab.tabElement.classList.add("highlight"); - }; - - WorkspaceView.prototype.unhighlightTab = function unhighlightTab(tab) { - - if (typeof tab === 'string') { - tab = this.findTab(tab); - } - - tab.tabElement.classList.remove("highlight"); + return this; }; // ========================================================================= @@ -473,6 +455,14 @@ // EVENT HANDLERS // ========================================================================= + const on_workspace_createoperator = function on_workspace_createoperator(workspace_model, operator) { + this.layout.content.appendChild(operator.wrapperElement); + }; + + const on_workspace_removeoperator = function on_workspace_removeoperator(workspace_model, operator) { + this.layout.content.removeChild(operator.wrapperElement); + }; + var on_workspace_change = function on_workspace_change(workspace) { var state; @@ -496,34 +486,32 @@ }; var on_workspace_unload = function on_workspace_unload(workspace) { - if (this.model === workspace) { - this.layout.content.clear(); - } + // This must be always the case + // if (this.model === workspace) { + this.layout.content.clear(); workspace.removeEventListener('remove', this.on_workspace_remove_bound); workspace.removeEventListener('change', this.on_workspace_change_bound); + workspace.removeEventListener('unload', this.on_workspace_unload_bound); + workspace.removeEventListener('createoperator', this.on_workspace_createoperator_bound); + workspace.removeEventListener('removeoperator', this.on_workspace_removeoperator_bound); + this.model = null; }; var on_click_createtab = function on_click_createtab(button) { button.disable(); - this.model.createTab().then(function (tab) { - this.notebook.createTab({ - tab_constructor: Wirecloud.ui.WorkspaceTabView, - model: tab, - workspace: this - }); - button.enable(); - }.bind(this), function () { - button.enable(); - }); - }; - - var get_tab_by_id = function get_tab_by_id(id) { - for (var i = 0; i < this.notebook.tabs.length; i++) { - if (this.tabs[i].id === id) { - return this.tabs[i]; + this.model.createTab().then( + (tab) => { + this.notebook.createTab({ + tab_constructor: Wirecloud.ui.WorkspaceTabView, + model: tab, + workspace: this + }); + button.enable(); + }, + () => { + button.enable(); } - } - return null; + ); }; Wirecloud.ui.WorkspaceView = WorkspaceView; diff --git a/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceViewMenuItems.js b/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceViewMenuItems.js index 0cb8c51496..8a81a51eaf 100644 --- a/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceViewMenuItems.js +++ b/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceViewMenuItems.js @@ -63,7 +63,7 @@ item = new se.MenuItem(utils.gettext("Rename"), function () { (new Wirecloud.ui.RenameWindowMenu(this, utils.gettext('Rename Workspace'))).show(); - }.bind(this.workspace)); + }.bind(this.workspace.model)); item.addIconClass("fa fa-pencil"); item.setDisabled(!this.workspace.model.isAllowed('rename')); items.push(item); @@ -81,7 +81,7 @@ dialog.show(); }.bind(this) }); - }.bind(this.workspace)); + }.bind(this.workspace.model)); item.addIconClass("fa fa-archive"); items.push(item); @@ -105,7 +105,7 @@ }) ); dialog.setHandler(() => { - return this.workspace.remove(); + return this.workspace.model.remove(); }).show(); }); item.addIconClass("fa fa-trash"); From 8feddd1410d1a3d0764aeda056f065d66608d8ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Arranz?= Date: Tue, 30 Oct 2018 22:37:26 +0100 Subject: [PATCH 02/22] Initial WorkspaceTabView unit tests --- src/GruntFile.js | 1 + src/js_tests/wirecloud/bootstrap.js | 1 + .../wirecloud/ui/WorkspaceTabViewSpec.js | 505 ++++++++++++++++++ .../js/wirecloud/ui/WorkspaceTabView.js | 61 +-- .../wirecloud/ui/WorkspaceTabViewMenuItems.js | 23 +- 5 files changed, 531 insertions(+), 60 deletions(-) create mode 100644 src/js_tests/wirecloud/ui/WorkspaceTabViewSpec.js diff --git a/src/GruntFile.js b/src/GruntFile.js index 1c911043c2..72a5735a7f 100644 --- a/src/GruntFile.js +++ b/src/GruntFile.js @@ -130,6 +130,7 @@ var WirecloudFiles = [ 'wirecloud/platform/static/js/wirecloud/ui/WiringEditor/Endpoint.js', 'wirecloud/platform/static/js/wirecloud/ui/WiringEditor/KeywordSuggestion.js', 'wirecloud/platform/static/js/wirecloud/ui/WorkspaceView.js', + 'wirecloud/platform/static/js/wirecloud/ui/WorkspaceTabView.js', 'wirecloud/platform/static/js/wirecloud/ui/SharingWindowMenu.js', 'wirecloud/platform/static/js/wirecloud/wiring/Endpoint.js', 'wirecloud/platform/static/js/wirecloud/wiring/EndpointTypeError.js', diff --git a/src/js_tests/wirecloud/bootstrap.js b/src/js_tests/wirecloud/bootstrap.js index 6fe26ef3c7..6a018bc6a5 100644 --- a/src/js_tests/wirecloud/bootstrap.js +++ b/src/js_tests/wirecloud/bootstrap.js @@ -17,6 +17,7 @@ const Wirecloud = { 'wirecloud/component_sidebar': ' ', 'wirecloud/wiring/behaviour_sidebar': '
', 'wirecloud/wiring/footer': ' ', + 'wirecloud/workspace/empty_tab_message': '
', 'wirecloud/workspace/sharing_user': '
', 'wirecloud/workspace/visibility_option': '
' } diff --git a/src/js_tests/wirecloud/ui/WorkspaceTabViewSpec.js b/src/js_tests/wirecloud/ui/WorkspaceTabViewSpec.js new file mode 100644 index 0000000000..2256c98f58 --- /dev/null +++ b/src/js_tests/wirecloud/ui/WorkspaceTabViewSpec.js @@ -0,0 +1,505 @@ +/* + * Copyright (c) 2018 Future Internet Consulting and Development Solutions S.L. + * + * This file is part of Wirecloud Platform. + * + * Wirecloud Platform is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * Wirecloud is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Wirecloud Platform. If not, see + * . + * + */ + +/* globals StyledElements, Wirecloud */ + + +(function (ns, utils) { + + "use strict"; + + const callEventListener = function callEventListener(instance, event) { + var largs = Array.prototype.slice.call(arguments, 2); + largs.unshift(instance); + instance.addEventListener.calls.allArgs().some(function (args) { + if (args[0] === event) { + args[1].apply(instance, largs); + return true; + } + }); + }; + + const create_tab = function create_tab(options) { + return utils.merge({ + addEventListener: jasmine.createSpy("addEventListener"), + createWidget: jasmine.createSpy("createWidget"), + id: "8", + preferences: { + get: jasmine.createSpy("get"), + addEventListener: jasmine.createSpy("addEventListener") + }, + title: "Tab Title", + widgets: [] + }, options); + }; + + const create_workspace = function create_workspace(options) { + let workspace = utils.merge({ + id: "1", + owner: "user", + name: "empty", + title: "Dashboard Title", + tabs: [ + { + id: "8", + widgets: [], + name: "tab", + title: "Tab", + initial: true + } + ], + operators: [], + preferences: {}, + createTab: jasmine.createSpy("createTab"), + isAllowed: jasmine.createSpy("isAllowed").and.returnValue(false) + }, options); + + workspace.addEventListener = jasmine.createSpy("addEventListener"); + workspace.removeEventListener = jasmine.createSpy("removeEventListener"); + + return { + buildAddWidgetButton: jasmine.createSpy("buildAddWidgetButton").and.returnValue(), + model: workspace + }; + }; + + describe("WorkspaceTabView", () => { + + const notebook = new StyledElements.Notebook(); + + beforeAll(() => { + // TODO + ns.WidgetView = jasmine.createSpy("WidgetView").and.callFake(function (tab, options) { + this.id = options.id; + this.load = jasmine.createSpy("load"); + }); + ns.WorkspaceTabViewDragboard = jasmine.createSpy("WorkspaceTabViewDragboard").and.callFake(function () { + this.freeLayout = { + adaptHeight: jasmine.createSpy("adaptHeight").and.returnValue({inLU: 0}), + adaptWidth: jasmine.createSpy("adaptWidth").and.returnValue({inLU: 0}), + adaptColumnOffset: jasmine.createSpy("adaptColumnOffset").and.returnValue({inLU: 0}), + adaptRowOffset: jasmine.createSpy("adaptRowOffset").and.returnValue({inLU: 0}) + }; + this.baseLayout = { + adaptHeight: jasmine.createSpy("adaptHeight").and.returnValue({inLU: 0}), + adaptWidth: jasmine.createSpy("adaptWidth").and.returnValue({inLU: 0}), + adaptColumnOffset: jasmine.createSpy("adaptColumnOffset").and.returnValue({inLU: 0}), + adaptRowOffset: jasmine.createSpy("adaptRowOffset").and.returnValue({inLU: 0}) + }; + this.paint = jasmine.createSpy("paint"); + this._notifyWindowResizeEvent = jasmine.createSpy("_notifyWindowResizeEvent"); + this._updateBaseLayout = jasmine.createSpy("_updateBaseLayout"); + }); + ns.WorkspaceTabViewMenuItems = jasmine.createSpy("WorkspaceTabViewMenuItems"); + utils.inherit(ns.WorkspaceTabViewMenuItems, StyledElements.DynamicMenuItems); + Wirecloud.TutorialCatalogue = { + buildTutorialReferences: jasmine.createSpy("buildTutorialReferences") + }; + }); + + afterAll(() => { + // TODO + ns.WidgetView = null; + ns.WorkspaceTabViewDragboard = null; + Wirecloud.TutorialCatalogue = null; + }); + + describe("new WorkspaceTabView(id, notebook, options)", () => { + + /* + it("should require the id parameter", () => { + expect(() => { + new ns.WorkspaceTabView(); + }).toThrowError(TypeError); + }); + */ + + it("should load empty tabs", () => { + let workspace = create_workspace(); + let model = create_tab(); + let tab = new ns.WorkspaceTabView("1", notebook, { + model: model, + workspace: workspace + }); + + expect(tab.title).toEqual("Tab Title"); + expect(tab.widgets).toEqual([]); + expect(tab.widgetsById).toEqual({}); + }); + + it("should load tabs with widgets", () => { + let workspace = create_workspace(); + let model = create_tab({ + widgets: [{id: "9"}, {id: "5"}] + }); + let tab = new ns.WorkspaceTabView("1", notebook, { + model: model, + workspace: workspace + }); + + expect(tab.title).toEqual("Tab Title"); + expect(tab.widgets).toEqual([ + jasmine.any(ns.WidgetView), + jasmine.any(ns.WidgetView) + ]); + expect(tab.widgetsById).toEqual({ + "5": jasmine.any(ns.WidgetView), + "9": jasmine.any(ns.WidgetView) + }); + }); + + it("should load editable tabs", () => { + let workspace = create_workspace(); + workspace.model.isAllowed.and.returnValue(true); + let model = create_tab({ + widgets: [{id: "9"}, {id: "5"}] + }); + ns.WorkspaceTabViewMenuItems.calls.reset(); + let tab = new ns.WorkspaceTabView("1", notebook, { + model: model, + workspace: workspace + }); + + expect(ns.WorkspaceTabViewMenuItems).toHaveBeenCalledWith(tab); + }); + + }); + + describe("createWidget(resource[, options])", () => { + + it("should commit changes by default", (done) => { + let workspace = create_workspace(); + let model = create_tab(); + let tab = new ns.WorkspaceTabView("1", notebook, { + model: model, + workspace: workspace + }); + let widgetmodel = {id: 80}; + model.createWidget.and.returnValue(Promise.resolve(widgetmodel)); + let widget = {id: 80}; + spyOn(tab, "findWidget").and.callFake((id) => { + expect(id).toBe(80); + return widget; + }); + // TODO this make sense? + tab.dragboard.freeLayout.adaptHeight.and.returnValue({inLU: "invalid"}); + tab.dragboard.freeLayout.adaptWidth.and.returnValue({inLU: 40}); + tab.dragboard.freeLayout.columns = 20; + // END TODO + + let p = tab.createWidget( + { + default_height: "120px", + default_width: "33%" + }, + { + layout: 1, + top: 2, + left: 3, + height: "invalid", + width: 40 + } + ); + + p.then((created_widget) => { + expect(created_widget).toBe(widget); + done(); + }); + }); + + it("should allow commit false", () => { + let workspace = create_workspace(); + let model = create_tab(); + let tab = new ns.WorkspaceTabView("1", notebook, { + model: model, + workspace: workspace + }); + let widgetmodel = {id: 80}; + model.createWidget.and.returnValue(widgetmodel); + let widget = {id: 80}; + spyOn(tab, "findWidget").and.callFake((id) => { + expect(id).toBe(80); + return widget; + }); + + let created_widget = tab.createWidget( + { + default_height: "120px", + default_width: "33%" + }, { + commit: false + } + ); + + expect(created_widget).toBe(widget); + }); + + it("should honour initiallayout preference", (done) => { + let workspace = create_workspace(); + let model = create_tab(); + model.preferences.get.and.returnValue("Free"); + let tab = new ns.WorkspaceTabView("1", notebook, { + model: model, + workspace: workspace + }); + let widgetmodel = {id: 80}; + model.createWidget.and.returnValue(Promise.resolve(widgetmodel)); + let widget = {id: 80}; + spyOn(tab, "findWidget").and.callFake((id) => { + expect(id).toBe(80); + return widget; + }); + tab.dragboard.freeLayout._searchFreeSpace = jasmine.createSpy("_searchFreeSpace").and.returnValue({x: 1, y: 2}); + + let p = tab.createWidget({ + default_height: "120px", + default_width: "33%" + }); + + p.then((created_widget) => { + expect(created_widget).toBe(widget); + done(); + }); + }); + + }); + + describe("highlight()", () => { + + it("should add the highlight CSS class", () => { + let workspace = create_workspace(); + let model = create_tab(); + let tab = new ns.WorkspaceTabView("1", notebook, { + model: model, + workspace: workspace + }); + + expect(tab.highlight()).toBe(tab); + }); + + }); + + describe("show()", () => { + + it("should load all the widgets from the tab", () => { + let workspace = create_workspace(); + let model = create_tab({ + widgets: [{}, {}] + }); + let tab = new ns.WorkspaceTabView("1", notebook, { + model: model, + workspace: workspace + }); + + expect(tab.show()).toBe(tab); + expect(tab.widgets[0].load).toHaveBeenCalled(); + expect(tab.widgets[1].load).toHaveBeenCalled(); + }); + + }); + + describe("showSettings()", () => { + + it("should display the settings dialog", () => { + var show; + // TODO + ns.PreferencesWindowMenu = jasmine.createSpy("PreferencesWindowMenu").and.callFake(function () { + this.show = show = jasmine.createSpy("show"); + }); + let workspace = create_workspace(); + let model = create_tab({ + widgets: [{}, {}] + }); + let tab = new ns.WorkspaceTabView("1", notebook, { + model: model, + workspace: workspace + }); + + expect(tab.showSettings()).toBe(tab); + + expect(ns.PreferencesWindowMenu).toHaveBeenCalledWith('tab', model.preferences); + expect(show).toHaveBeenCalledWith(); + }); + + }); + + describe("tab events", () => { + + var workspace, model, tab; + + beforeEach(() => { + workspace = create_workspace(); + model = create_tab({}); + tab = new ns.WorkspaceTabView("1", notebook, { + model: model, + workspace: workspace + }); + }); + + it("should ignore preferences changes not related to the baselayout", () => { + callEventListener(model.preferences, "pre-commit", {"other": 5}); + + expect(tab.dragboard._updateBaseLayout).not.toHaveBeenCalled(); + }); + + it("should handle changes to the baselayout preference", () => { + callEventListener(model.preferences, "pre-commit", {"baselayout": 5}); + + expect(tab.dragboard._updateBaseLayout).toHaveBeenCalledWith(); + }); + + it("should handle title changes", () => { + spyOn(StyledElements.Tab.prototype, "rename"); + let newtitle = "NewTitle"; + model.title = newtitle; + + callEventListener(model, "change", ['title']); + + expect(StyledElements.Tab.prototype.rename).toHaveBeenCalledWith(newtitle); + }); + + it("should handle name changes (visible tab)", () => { + spyOn(StyledElements.Tab.prototype, "rename"); + spyOn(Wirecloud.HistoryManager, "getCurrentState").and.returnValue({ + workspace_owner: "owner", + workspace_name: "dashboard", + tab: "oldname" + }); + spyOn(Wirecloud.HistoryManager, "replaceState"); + let newname = "new-name"; + model.name = newname; + tab.hidden = false; + + callEventListener(model, "change", ['name']); + + expect(StyledElements.Tab.prototype.rename).not.toHaveBeenCalled(); + expect(Wirecloud.HistoryManager.replaceState).toHaveBeenCalledWith({ + workspace_owner: "owner", + workspace_name: "dashboard", + tab: newname + }); + }); + + it("should handle name changes (hidden tab)", () => { + spyOn(StyledElements.Tab.prototype, "rename"); + spyOn(Wirecloud.HistoryManager, "getCurrentState"); + spyOn(Wirecloud.HistoryManager, "replaceState"); + let newname = "new-name"; + model.name = newname; + tab.hidden = true; + + callEventListener(model, "change", ['name']); + + expect(StyledElements.Tab.prototype.rename).not.toHaveBeenCalled(); + expect(Wirecloud.HistoryManager.replaceState).not.toHaveBeenCalled(); + }); + + it("should handle remove events", () => { + spyOn(StyledElements.Tab.prototype, "close"); + + callEventListener(model, "remove"); + + expect(StyledElements.Tab.prototype.close).toHaveBeenCalledWith(); + }); + + it("should handle createwidget events (visible tab)", () => { + let workspace = create_workspace(); + let model = create_tab(); + let tab = new ns.WorkspaceTabView("1", notebook, { + model: model, + workspace: workspace + }); + let widgetmodel = {id: "20"}; + tab.hidden = false; + + callEventListener(model, "createwidget", widgetmodel); + + expect(tab.widgets).toEqual([ + jasmine.any(ns.WidgetView) + ]); + expect(tab.widgetsById).toEqual({ + "20": jasmine.any(ns.WidgetView) + }); + expect(tab.widgets[0].load).toHaveBeenCalledWith(); + }); + + it("should handle createwidget events (hidden tab)", () => { + let workspace = create_workspace(); + let model = create_tab(); + let tab = new ns.WorkspaceTabView("1", notebook, { + model: model, + workspace: workspace + }); + let widgetmodel = {id: "20"}; + tab.hidden = true; + + callEventListener(model, "createwidget", widgetmodel); + + expect(tab.widgets).toEqual([ + jasmine.any(ns.WidgetView) + ]); + expect(tab.widgetsById).toEqual({ + "20": jasmine.any(ns.WidgetView) + }); + expect(tab.widgets[0].load).not.toHaveBeenCalled(); + }); + + it("should handle removewidget events", () => { + let workspace = create_workspace(); + let model = create_tab({ + widgets: [{id: "9"}, {id: "5"}] + }); + let tab = new ns.WorkspaceTabView("1", notebook, { + model: model, + workspace: workspace + }); + let widget = tab.findWidget("5"); + + callEventListener(model, "removewidget", widget); + + expect(tab.widgets).toEqual([ + jasmine.any(ns.WidgetView) + ]); + expect(tab.widgetsById).toEqual({ + "9": jasmine.any(ns.WidgetView) + }); + }); + + }); + + describe("unhighlight()", () => { + + it("should remove the highlight CSS class", () => { + let workspace = create_workspace(); + let model = create_tab(); + let tab = new ns.WorkspaceTabView("1", notebook, { + model: model, + workspace: workspace + }); + + expect(tab.unhighlight()).toBe(tab); + }); + + }); + + }); + +})(Wirecloud.ui, StyledElements.Utils); diff --git a/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceTabView.js b/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceTabView.js index a1dad64534..ee2b971021 100644 --- a/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceTabView.js +++ b/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceTabView.js @@ -122,7 +122,7 @@ this.wrapperElement.classList.add("wc-workspace-tab-content"); this.wrapperElement.setAttribute('data-id', this.id); - if (!this.workspace.model.restricted) { + if (this.workspace.model.isAllowed("edit")) { var button = new se.PopupButton({ title: utils.gettext("Preferences"), class: 'icon-tab-menu', @@ -198,14 +198,11 @@ return this.findWidget(this.model.createWidget(resource, options).id); } - return new Promise(function (resolve, reject) { - - this.model.createWidget(resource, options).then(function (model) { - resolve(this.findWidget(model.id)); - }.bind(this), function (reason) { - reject(reason); - }); - }.bind(this)); + return this.model.createWidget(resource, options).then( + (model) => { + return Promise.resolve(this.findWidget(model.id)); + } + ); }, /** @@ -213,6 +210,7 @@ */ highlight: function () { this.tabElement.classList.add("highlight"); + return this; }, /** @@ -224,54 +222,16 @@ return this.widgetsById[id]; }, - /** - * @returns {Promise} - */ - remove: function remove() { - - if (privates.get(this).widgets.length) { - var dialog = new Wirecloud.ui.AlertWindowMenu(utils.gettext("The tab's widgets will also be removed. Would you like to continue?")); - dialog.setHandler(() => { - _remove.call(this); - }).show(); - } else { - _remove.call(this); - } - - return this; - }, - - /** - * Renames this tab - * - * @param {String} name - * - * @returns {Promise} - */ - rename: function rename(name) { - return this.model.rename(name).catch((reason) => { - this.logManager.log(reason); - return Promise.reject(reason); - }); - }, - repaint: function repaint() { this.dragboard.paint(); this.dragboard._notifyWindowResizeEvent(); return this; }, - /** - * @returns {Promise} - */ - setInitial: function setInitial() { - return this.model.setInitial(); - }, - show: function show() { se.Tab.prototype.show.call(this); - this.widgets.forEach(function (widget) { + privates.get(this).widgets.forEach(function (widget) { widget.load(); }); @@ -285,6 +245,7 @@ unhighlight: function unhighlight() { this.tabElement.classList.remove("highlight"); + return this; } }); @@ -305,10 +266,6 @@ return widget; }; - var _remove = function _remove() { - this.model.remove(); - }; - var clean_number = function clean_number(value, min, max) { if (typeof value !== 'number' || value < min) { diff --git a/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceTabViewMenuItems.js b/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceTabViewMenuItems.js index 2cb43f72ec..f4544f1491 100644 --- a/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceTabViewMenuItems.js +++ b/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceTabViewMenuItems.js @@ -54,15 +54,15 @@ items = []; - item = new se.MenuItem(utils.gettext("Rename"), function () { - (new Wirecloud.ui.RenameWindowMenu(this, utils.gettext('Rename Workspace Tab'))).show(); - }.bind(this.tab)); + item = new se.MenuItem(utils.gettext("Rename"), () => { + (new Wirecloud.ui.RenameWindowMenu(this.tab.model, utils.gettext('Rename Workspace Tab'))).show(); + }); item.addIconClass("fa fa-pencil"); items.push(item); item = new se.MenuItem(utils.gettext("Set as initial"), function () { - this.setInitial(); - }.bind(this.tab)); + this.tab.model.setInitial(); + }); item.addIconClass("fa fa-home"); item.setDisabled(this.tab.model.initial); items.push(item); @@ -73,9 +73,16 @@ item.addIconClass("fa fa-cog"); items.push(item); - item = new se.MenuItem(utils.gettext("Remove"), function () { - this.remove(); - }.bind(this.tab)); + item = new se.MenuItem(utils.gettext("Remove"), () => { + if (this.tab.widgets.length) { + var dialog = new Wirecloud.ui.AlertWindowMenu(utils.gettext("The tab's widgets will also be removed. Would you like to continue?")); + dialog.setHandler(() => { + this.tab.model.remove(); + }).show(); + } else { + this.tab.model.remove(); + } + }); item.addIconClass("fa fa-trash"); item.setDisabled(!this.tab.model.isAllowed('remove')); items.push(item); From 3f92055cc3b235df17048911a2aff7c09262136a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Arranz?= Date: Tue, 6 Nov 2018 22:10:27 +0100 Subject: [PATCH 03/22] Initial WorkspaceTabViewDragboard unit tests --- src/GruntFile.js | 1 + src/js_tests/wirecloud/WiringSpec.js | 1 + src/js_tests/wirecloud/bootstrap.js | 1 + .../ui/WorkspaceTabViewDragboardSpec.js | 691 ++++++++++++++++++ .../platform/static/js/wirecloud/Wiring.js | 2 +- .../wirecloud/ui/WorkspaceTabViewDragboard.js | 79 +- 6 files changed, 733 insertions(+), 42 deletions(-) create mode 100644 src/js_tests/wirecloud/ui/WorkspaceTabViewDragboardSpec.js diff --git a/src/GruntFile.js b/src/GruntFile.js index 72a5735a7f..05f3413931 100644 --- a/src/GruntFile.js +++ b/src/GruntFile.js @@ -131,6 +131,7 @@ var WirecloudFiles = [ 'wirecloud/platform/static/js/wirecloud/ui/WiringEditor/KeywordSuggestion.js', 'wirecloud/platform/static/js/wirecloud/ui/WorkspaceView.js', 'wirecloud/platform/static/js/wirecloud/ui/WorkspaceTabView.js', + 'wirecloud/platform/static/js/wirecloud/ui/WorkspaceTabViewDragboard.js', 'wirecloud/platform/static/js/wirecloud/ui/SharingWindowMenu.js', 'wirecloud/platform/static/js/wirecloud/wiring/Endpoint.js', 'wirecloud/platform/static/js/wirecloud/wiring/EndpointTypeError.js', diff --git a/src/js_tests/wirecloud/WiringSpec.js b/src/js_tests/wirecloud/WiringSpec.js index 25fa98ab34..8dbb46c0ba 100644 --- a/src/js_tests/wirecloud/WiringSpec.js +++ b/src/js_tests/wirecloud/WiringSpec.js @@ -540,6 +540,7 @@ ); }); + }); describe("findOperator(id)", function () { diff --git a/src/js_tests/wirecloud/bootstrap.js b/src/js_tests/wirecloud/bootstrap.js index 6a018bc6a5..89f2641811 100644 --- a/src/js_tests/wirecloud/bootstrap.js +++ b/src/js_tests/wirecloud/bootstrap.js @@ -30,6 +30,7 @@ const Wirecloud = { }, ui: {}, URLs: { + IWIDGET_COLLECTION: new StyledElements.Utils.Template("/api/workspace/%(workspace_id)s/tab/%(tab_id)s/iwidgets"), IWIDGET_ENTRY: new StyledElements.Utils.Template("/api/workspace/%(workspace_id)s/tab/%(tab_id)s/iwidget/%(iwidget_id)s"), IWIDGET_PREFERENCES: new StyledElements.Utils.Template("/api/workspace/%(workspace_id)s/tab/%(tab_id)s/iwidget/%(iwidget_id)s/preferences"), IWIDGET_PROPERTIES: new StyledElements.Utils.Template("/api/workspace/%(workspace_id)s/tab/%(tab_id)s/iwidget/%(iwidget_id)s/properties"), diff --git a/src/js_tests/wirecloud/ui/WorkspaceTabViewDragboardSpec.js b/src/js_tests/wirecloud/ui/WorkspaceTabViewDragboardSpec.js new file mode 100644 index 0000000000..b6bce0c337 --- /dev/null +++ b/src/js_tests/wirecloud/ui/WorkspaceTabViewDragboardSpec.js @@ -0,0 +1,691 @@ +/* + * Copyright (c) 2018 Future Internet Consulting and Development Solutions S.L. + * + * This file is part of Wirecloud Platform. + * + * Wirecloud Platform is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * Wirecloud is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Wirecloud Platform. If not, see + * . + * + */ + +/* globals StyledElements, Wirecloud */ + + +(function (ns, utils) { + + "use strict"; + + const create_tab = function () { + return { + appendChild: jasmine.createSpy("appendChild"), + model: { + id: 3, + preferences: { + get: jasmine.createSpy("get").and.returnValue({ + "type": "gridlayout", + "columns": 20 + }) + } + }, + removeChild: jasmine.createSpy("removeChild"), + workspace: { + model: { + id: 8 + }, + restricted: false + } + }; + }; + + const create_widget = function (options) { + if (options == null) { + options = {}; + } + if (!("volatile" in options)) { + options.volatile = false; + } + if (!("z" in options)) { + options.z = null; + } + if (options.id == null) { + options.id = "1"; + } + + return { + id: options.id, + model: { + volatile: !!options.volatile + }, + persist: jasmine.createSpy("persist"), + position: { + z: options.z + }, + setPosition: jasmine.createSpy("setPosition").and.callFake(function (options) { + this.position.z = options.z; + }) + }; + }; + + const layout_constructor = function () { + this.destroy = jasmine.createSpy("destroy"); + this.initialize = jasmine.createSpy("initialize"); + this.moveTo = jasmine.createSpy("moveTo"); + this._notifyWindowResizeEvent = jasmine.createSpy("_notifyWindowResizeEvent"); + }; + + describe("WorkspaceTabViewDragboard", () => { + + beforeAll(() => { + Wirecloud.ui.ColumnLayout = jasmine.createSpy("ColumnLayout").and.callFake(layout_constructor); + Wirecloud.ui.GridLayout = jasmine.createSpy("GridLayout").and.callFake(layout_constructor); + Wirecloud.ui.SmartColumnLayout = jasmine.createSpy("SmartColumnLayout").and.callFake(layout_constructor); + Wirecloud.ui.FreeLayout = jasmine.createSpy("FreeLayout").and.callFake(layout_constructor); + Wirecloud.ui.FullDragboardLayout = jasmine.createSpy("FullDragboardLayout").and.callFake(layout_constructor); + Wirecloud.ui.SidebarLayout = jasmine.createSpy("SidebarLayout"); + }); + + describe("new WorkspaceTabViewDragboard", () => { + + it("should start empty", () => { + let tab = create_tab(); + let dragboard = new ns.WorkspaceTabViewDragboard(tab); + expect(dragboard.tab).toBe(tab); + expect(dragboard.widgets).toEqual([]); + expect(dragboard.baseLayout).toEqual(jasmine.any(Wirecloud.ui.GridLayout)); + }); + + it("should configure base layout", () => { + let tab = create_tab(); + tab.model.preferences.get.and.returnValue({ + "type": "columnlayout", + "smart": true, + "columns": 20 + }) + let dragboard = new ns.WorkspaceTabViewDragboard(tab); + expect(dragboard.tab).toBe(tab); + expect(dragboard.widgets).toEqual([]); + expect(dragboard.baseLayout).toEqual(jasmine.any(Wirecloud.ui.SmartColumnLayout)); + }); + + }); + + describe("_addWidget(widget)", () => { + + it("should add widget on top if widget is not z-positioned", () => { + let tab = create_tab(); + let dragboard = new ns.WorkspaceTabViewDragboard(tab); + let widget = create_widget(); + + dragboard._addWidget(widget); + + expect(dragboard.tab).toBe(tab); + expect(dragboard.tab.appendChild).toHaveBeenCalledWith(widget); + expect(dragboard.widgets).toEqual([widget]); + expect(widget.setPosition).toHaveBeenCalledWith({ + z: 0 + }); + }); + + it("should move widgets when z position is already taken", () => { + let tab = create_tab(); + let dragboard = new ns.WorkspaceTabViewDragboard(tab); + let widget1 = create_widget({z: 0}); + dragboard._addWidget(widget1); + let widget2 = create_widget({z: 1}); + dragboard._addWidget(widget2); + let widget3 = create_widget({z: 2}); + dragboard._addWidget(widget3); + let newwidget = create_widget({z: 1}); + widget1.setPosition.calls.reset(); + widget2.setPosition.calls.reset(); + widget3.setPosition.calls.reset(); + + dragboard._addWidget(newwidget); + + expect(dragboard.tab).toBe(tab); + expect(dragboard.widgets).toEqual([widget1, widget2, newwidget, widget3]); + expect(widget1.setPosition).toHaveBeenCalledWith({ + z: 0 + }); + expect(widget2.setPosition).toHaveBeenCalledWith({ + z: 1 + }); + expect(newwidget.setPosition).toHaveBeenCalledWith({ + z: 2 + }); + expect(widget3.setPosition).toHaveBeenCalledWith({ + z: 3 + }); + }); + + it("should move widgets when z position is already taken (should work with holes)", () => { + // Holes are only accepted while loading the dashboard as widgets can be loaded without taking into account the z-index order, once loaded, holes are not accepted + let tab = create_tab(); + let dragboard = new ns.WorkspaceTabViewDragboard(tab); + let widget1 = create_widget({z: 0}); + dragboard._addWidget(widget1); + let widget2 = create_widget({z: 1}); + dragboard._addWidget(widget2); + let widget3 = create_widget({z: 4}); + dragboard._addWidget(widget3); + let newwidget = create_widget({z: 1}); + widget1.setPosition.calls.reset(); + widget2.setPosition.calls.reset(); + widget3.setPosition.calls.reset(); + + dragboard._addWidget(newwidget); + + expect(dragboard.tab).toBe(tab); + // property 3 and 4 should not exist (this is different to be + // undefined) + let expected_array = [widget1, widget2, newwidget]; + expected_array[5] = widget3; + expect(dragboard.widgets).toEqual(expected_array); + expect(widget1.setPosition).toHaveBeenCalledWith({ + z: 0 + }); + expect(widget2.setPosition).toHaveBeenCalledWith({ + z: 1 + }); + expect(newwidget.setPosition).toHaveBeenCalledWith({ + z: 2 + }); + expect(widget3.setPosition).toHaveBeenCalledWith({ + z: 5 + }); + }); + + it("should add the fixed class when restricted", () => { + let tab = create_tab(); + tab.wrapperElement = { + classList: { + add: jasmine.createSpy("fixed") + } + }; + tab.workspace.restricted = true; + let dragboard = new ns.WorkspaceTabViewDragboard(tab); + expect(dragboard.tab).toBe(tab); + expect(tab.wrapperElement.classList.add).toHaveBeenCalledWith("fixed"); + }); + + }); + + describe("getWidth() & getHeight()", () => { + + it("getHeight()", () => { + let tab = create_tab(); + let dragboard = new ns.WorkspaceTabViewDragboard(tab); + dragboard.dragboardHeight = 20; + expect(dragboard.getHeight()).toBe(20); + }); + + it("getWidth()", () => { + let tab = create_tab(); + let dragboard = new ns.WorkspaceTabViewDragboard(tab); + dragboard.dragboardWidth = 20; + expect(dragboard.getWidth()).toBe(20); + }); + + }); + + describe("lower(widget)", () => { + + var dragboard, widget1, widget2, widget3; + + beforeEach(() => { + let tab = create_tab(); + dragboard = new ns.WorkspaceTabViewDragboard(tab); + widget1 = create_widget(); + dragboard._addWidget(widget1); + widget2 = create_widget(); + dragboard._addWidget(widget2); + widget3 = create_widget(); + dragboard._addWidget(widget3); + widget1.setPosition.calls.reset(); + widget2.setPosition.calls.reset(); + widget3.setPosition.calls.reset(); + }); + + it("should do nothing if the widget to lower is already the lowest one", () => { + expect(dragboard.lower(widget1)).toBe(dragboard); + + expect(dragboard.widgets).toEqual([widget1, widget2, widget3]); + expect(widget1.setPosition).not.toHaveBeenCalled(); + }); + + it("should lower widget and update the affected widget", () => { + expect(dragboard.lower(widget2)).toBe(dragboard); + + expect(dragboard.widgets).toEqual([widget2, widget1, widget3]); + expect(widget1.setPosition).toHaveBeenCalledWith({ + z: 1 + }); + expect(widget2.setPosition).toHaveBeenCalledWith({ + z: 0 + }); + expect(widget3.setPosition).not.toHaveBeenCalled(); + }); + + }); + + describe("lowerToBottom(widget)", () => { + + var dragboard, widget1, widget2, widget3; + + beforeEach(() => { + let tab = create_tab(); + dragboard = new ns.WorkspaceTabViewDragboard(tab); + widget1 = create_widget(); + dragboard._addWidget(widget1); + widget2 = create_widget(); + dragboard._addWidget(widget2); + widget3 = create_widget(); + dragboard._addWidget(widget3); + widget1.setPosition.calls.reset(); + widget2.setPosition.calls.reset(); + widget3.setPosition.calls.reset(); + spyOn(dragboard, "update"); + }); + + it("should do nothing if the widget to lower is already the lowest one", () => { + expect(dragboard.lowerToBottom(widget1)).toBe(dragboard); + + expect(dragboard.widgets).toEqual([widget1, widget2, widget3]); + expect(widget1.setPosition).not.toHaveBeenCalled(); + expect(dragboard.update).not.toHaveBeenCalled(); + }); + + it("should lower widget and update the affected widgets", () => { + expect(dragboard.lowerToBottom(widget3)).toBe(dragboard); + + expect(dragboard.widgets).toEqual([widget3, widget1, widget2]); + expect(widget1.setPosition).toHaveBeenCalledWith({ + z: 1 + }); + expect(widget2.setPosition).toHaveBeenCalledWith({ + z: 2 + }); + expect(widget3.setPosition).toHaveBeenCalledWith({ + z: 0 + }); + expect(dragboard.update).toHaveBeenCalledWith(); + }); + + }); + + describe("paint()", () => { + + it("should compress z indexes", () => { + let tab = create_tab(); + let dragboard = new ns.WorkspaceTabViewDragboard(tab); + let widget1 = create_widget({z: 0}); + dragboard._addWidget(widget1); + let widget3 = create_widget({z: 2}); + dragboard._addWidget(widget3); + widget1.setPosition.calls.reset(); + widget3.setPosition.calls.reset(); + spyOn(dragboard, "_recomputeSize"); + + dragboard.paint(); + + expect(dragboard.widgets).toEqual([widget1, widget3]); + expect(widget1.setPosition).toHaveBeenCalledWith({ + z: 0 + }); + expect(widget3.setPosition).toHaveBeenCalledWith({ + z: 1 + }); + }); + + it("should do nothing if already painted", () => { + let tab = create_tab(); + let dragboard = new ns.WorkspaceTabViewDragboard(tab); + let widget1 = create_widget({z: 0}); + dragboard._addWidget(widget1); + let widget3 = create_widget({z: 2}); + dragboard._addWidget(widget3); + spyOn(dragboard, "_recomputeSize"); + + dragboard.paint(); + widget1.setPosition.calls.reset(); + widget3.setPosition.calls.reset(); + dragboard.paint(); + + expect(dragboard.widgets).toEqual([widget1, widget3]); + expect(widget1.setPosition).not.toHaveBeenCalled(); + expect(widget3.setPosition).not.toHaveBeenCalled(); + }); + }); + + describe("raise(widget)", () => { + + var dragboard, widget1, widget2, widget3; + + beforeEach(() => { + let tab = create_tab(); + dragboard = new ns.WorkspaceTabViewDragboard(tab); + widget1 = create_widget(); + dragboard._addWidget(widget1); + widget2 = create_widget(); + dragboard._addWidget(widget2); + widget3 = create_widget(); + dragboard._addWidget(widget3); + widget1.setPosition.calls.reset(); + widget2.setPosition.calls.reset(); + widget3.setPosition.calls.reset(); + }); + + it("should do nothing if the widget to raise is already the upper one", () => { + expect(dragboard.raise(widget3)).toBe(dragboard); + + expect(dragboard.widgets).toEqual([widget1, widget2, widget3]); + expect(widget3.setPosition).not.toHaveBeenCalled(); + }); + + it("should raise widget and update the affected widget", () => { + expect(dragboard.raise(widget2)).toBe(dragboard); + + expect(dragboard.widgets).toEqual([widget1, widget3, widget2]); + expect(widget2.setPosition).toHaveBeenCalledWith({ + z: 2 + }); + expect(widget3.setPosition).toHaveBeenCalledWith({ + z: 1 + }); + expect(widget1.setPosition).not.toHaveBeenCalled(); + }); + + }); + + describe("raiseToTop(widget)", () => { + + var dragboard, widget1, widget2, widget3; + + beforeEach(() => { + let tab = create_tab(); + dragboard = new ns.WorkspaceTabViewDragboard(tab); + widget1 = create_widget(); + dragboard._addWidget(widget1); + widget2 = create_widget(); + dragboard._addWidget(widget2); + widget3 = create_widget(); + dragboard._addWidget(widget3); + widget1.setPosition.calls.reset(); + widget2.setPosition.calls.reset(); + widget3.setPosition.calls.reset(); + spyOn(dragboard, "update"); + }); + + it("should do nothing if the widget to raise is already the highest one", () => { + expect(dragboard.raiseToTop(widget3)).toBe(dragboard); + + expect(dragboard.widgets).toEqual([widget1, widget2, widget3]); + expect(widget3.setPosition).not.toHaveBeenCalled(); + expect(dragboard.update).not.toHaveBeenCalled(); + }); + + it("should raise widget and update the affected widgets", () => { + expect(dragboard.raiseToTop(widget1)).toBe(dragboard); + + expect(dragboard.widgets).toEqual([widget2, widget3, widget1]); + expect(widget1.setPosition).toHaveBeenCalledWith({ + z: 2 + }); + expect(widget2.setPosition).toHaveBeenCalledWith({ + z: 0 + }); + expect(widget3.setPosition).toHaveBeenCalledWith({ + z: 1 + }); + expect(dragboard.update).toHaveBeenCalledWith(); + }); + + }); + + describe("_removeWidget(widget)", () => { + + it("should add widget on top if widget is not z-positioned", () => { + let tab = create_tab(); + let dragboard = new ns.WorkspaceTabViewDragboard(tab); + let widget1 = create_widget({"id": "1"}); + dragboard._addWidget(widget1); + let widget2 = create_widget({"id": "2"}); + dragboard._addWidget(widget2); + let widget3 = create_widget({"id": "3"}); + dragboard._addWidget(widget3); + + dragboard._removeWidget(widget2); + + expect(dragboard.widgets).toEqual([widget1, widget3]); + expect(dragboard.tab.removeChild).toHaveBeenCalledWith(widget2); + }); + + }); + + describe("_recomputeSize()", () => { + + it("DEPRECATED recompute size (display: none)", () => { + let tab = create_tab(); + let dragboard = new ns.WorkspaceTabViewDragboard(tab); + spyOn(document.defaultView, "getComputedStyle").and.returnValue({ + getPropertyValue: jasmine.createSpy("getPropertyValue").and.returnValue("none"), + }); + + dragboard._recomputeSize(); + }); + + it("DEPRECATED recompute size", () => { + let parentElement = document.createElement('div'); + let tab = create_tab(); + tab.wrapperElement = document.createElement('div'); + parentElement.appendChild(tab.wrapperElement); + let dragboard = new ns.WorkspaceTabViewDragboard(tab); + spyOn(document.defaultView, "getComputedStyle").and.returnValue({ + getPropertyValue: jasmine.createSpy("getPropertyValue").and.returnValue("block"), + getPropertyCSSValue: () => { + return { + getFloatValue: () => {} + }; + } + }); + + dragboard._recomputeSize(); + }); + + }); + + describe("_notifyWindowResizeEvent()", () => { + + it("DEPRECATED resize event", () => { + let tab = create_tab(); + let dragboard = new ns.WorkspaceTabViewDragboard(tab); + spyOn(dragboard, "_recomputeSize").and.callFake(function () { + this.dragboardWidth = 50; + this.dragboardHeight = 20; + }); + spyOn(dragboard, "_updateIWidgetSizes"); + + dragboard._notifyWindowResizeEvent(); + + expect(dragboard._recomputeSize).toHaveBeenCalledWith(); + expect(dragboard._updateIWidgetSizes).toHaveBeenCalledWith(true, true); + }); + + it("DEPRECATED resize event (no change)", () => { + let tab = create_tab(); + let dragboard = new ns.WorkspaceTabViewDragboard(tab); + spyOn(dragboard, "_recomputeSize"); + + dragboard._notifyWindowResizeEvent(); + + expect(dragboard._recomputeSize).toHaveBeenCalledWith(); + }); + + }); + + describe("_updateBaseLayout()", () => { + + it("should update base layout", () => { + let tab = create_tab(); + let dragboard = new ns.WorkspaceTabViewDragboard(tab); + let initial_base_layout = dragboard.baseLayout; + tab.model.preferences.get.and.returnValue({ + "type": "columnlayout", + "smart": false, + "columns": 20 + }) + + dragboard._updateBaseLayout(); + + expect(dragboard.baseLayout).not.toBe(initial_base_layout); + expect(dragboard.baseLayout.initialize).toHaveBeenCalledWith(); + expect(initial_base_layout.moveTo).toHaveBeenCalledWith(dragboard.baseLayout); + expect(initial_base_layout.destroy).toHaveBeenCalledWith(); + }); + + }); + + describe("_updateIWidgetSizes(widthChanged, heightChanged)", () => { + + it("should update all the layouts", () => { + let tab = create_tab(); + let dragboard = new ns.WorkspaceTabViewDragboard(tab); + + dragboard._updateIWidgetSizes(true, false); + + expect(dragboard.baseLayout._notifyWindowResizeEvent).toHaveBeenCalledWith(true, false); + expect(dragboard.freeLayout._notifyWindowResizeEvent).toHaveBeenCalledWith(true, false); + expect(dragboard.fulldragboardLayout._notifyWindowResizeEvent).toHaveBeenCalledWith(true, false); + }); + + }); + + describe("update([ids])", () => { + + var dragboard, widget1, widget2, widget3; + + beforeEach(() => { + let tab = create_tab(); + dragboard = new ns.WorkspaceTabViewDragboard(tab); + widget1 = create_widget({"id": "1"}); + dragboard._addWidget(widget1); + widget2 = create_widget({"id": "2"}); + dragboard._addWidget(widget2); + widget3 = create_widget({"id": "3"}); + dragboard._addWidget(widget3); + tab.widgetsById = { + "1": widget1, + "2": widget2, + "3": widget3 + }; + }); + + it("should persist all widgets when no passing the ids parameter", (done) => { + spyOn(Wirecloud.io, "makeRequest").and.callFake((url, options) => { + return new Wirecloud.Task("Sending request", (resolve) => {resolve({status: 204});}); + }); + + let p = dragboard.update(); + + p.then(() => { + expect(Wirecloud.io.makeRequest).toHaveBeenCalled(); + let body = JSON.parse(Wirecloud.io.makeRequest.calls.argsFor(0)[1].postBody); + expect(body.length).toBe(3); + done(); + }, fail); + }); + + it("should allow to filter widgets", (done) => { + spyOn(Wirecloud.io, "makeRequest").and.callFake((url, options) => { + return new Wirecloud.Task("Sending request", (resolve) => {resolve({status: 204});}); + }); + + let p = dragboard.update(["1"]); + + p.then(() => { + expect(Wirecloud.io.makeRequest).toHaveBeenCalled(); + let body = JSON.parse(Wirecloud.io.makeRequest.calls.argsFor(0)[1].postBody); + expect(body.length).toBe(1); + done(); + }, fail); + }); + + it("should do nothing if the filtered list of widget is empty", (done) => { + spyOn(Wirecloud.io, "makeRequest").and.callFake((url, options) => { + return new Wirecloud.Task("Sending request", (resolve) => {resolve({status: 204});}); + }); + + let p = dragboard.update(["4", "5"]); + + p.then(() => { + expect(Wirecloud.io.makeRequest).not.toHaveBeenCalled(); + done(); + }, fail); + }); + + it("handles unexpected responses", (done) => { + + spyOn(Wirecloud.io, "makeRequest").and.callFake(function (url, options) { + expect(options.method).toEqual("PUT"); + return new Wirecloud.Task("Sending request", function (resolve) { + resolve({ + status: 201 + }); + }); + }); + + let task = dragboard.update(); + + task.then( + (value) => { + fail("success callback called"); + }, + (error) => { + expect(Wirecloud.io.makeRequest).toHaveBeenCalled(); + done(); + } + ); + + }); + + it("handles error responses", (done) => { + + spyOn(Wirecloud.io, "makeRequest").and.callFake(function (url, options) { + expect(options.method).toEqual("PUT"); + return new Wirecloud.Task("Sending request", function (resolve) { + resolve({ + status: 403 + }); + }); + }); + + let task = dragboard.update(); + + task.then( + (value) => { + fail("success callback called"); + }, + (error) => { + expect(Wirecloud.io.makeRequest).toHaveBeenCalled(); + done(); + } + ); + + }); + + }); + + }); + +})(Wirecloud.ui, StyledElements.Utils); diff --git a/src/wirecloud/platform/static/js/wirecloud/Wiring.js b/src/wirecloud/platform/static/js/wirecloud/Wiring.js index 9f53564c3a..e76d96f261 100644 --- a/src/wirecloud/platform/static/js/wirecloud/Wiring.js +++ b/src/wirecloud/platform/static/js/wirecloud/Wiring.js @@ -668,4 +668,4 @@ } }; -})(Wirecloud, StyledElements, StyledElements.Utils); +})(Wirecloud, StyledElements, Wirecloud.Utils); diff --git a/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceTabViewDragboard.js b/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceTabViewDragboard.js index e2d7d6e40e..b08c882310 100644 --- a/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceTabViewDragboard.js +++ b/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceTabViewDragboard.js @@ -1,5 +1,6 @@ /* * Copyright (c) 2008-2016 CoNWeT Lab., Universidad PolitĆ©cnica de Madrid + * Copyright (c) 2018 Future Internet Consulting and Development Solutions S.L. * * This file is part of Wirecloud Platform. * @@ -22,7 +23,7 @@ /* globals CSSPrimitiveValue, Wirecloud */ -(function (ns) { +(function (ns, utils) { "use strict"; @@ -182,39 +183,38 @@ }; WorkspaceTabViewDragboard.prototype.update = function update(ids) { - return new Promise(function (resolve, reject) { - var url = Wirecloud.URLs.IWIDGET_COLLECTION.evaluate({ - workspace_id: this.tab.workspace.model.id, - tab_id: this.tab.model.id - }); + var url = Wirecloud.URLs.IWIDGET_COLLECTION.evaluate({ + workspace_id: this.tab.workspace.model.id, + tab_id: this.tab.model.id + }); - ids = ids || Object.keys(this.tab.widgetsById); + ids = ids || Object.keys(this.tab.widgetsById); - var content = this.widgets.filter(function (widget) { - return !widget.model.volatile && ids.indexOf(widget.id) !== -1; - }); + var content = this.widgets.filter(function (widget) { + return !widget.model.volatile && ids.indexOf(widget.id) !== -1; + }); - if (!content.length) { - return resolve(this); + if (!content.length) { + return Promise.resolve(this); + } + + return Wirecloud.io.makeRequest(url, { + method: 'PUT', + requestHeaders: {'Accept': 'application/json'}, + contentType: 'application/json', + postBody: JSON.stringify(content) + }).then((response) => { + if ([204, 401, 403, 404, 500].indexOf(response.status) === -1) { + return Promise.reject(utils.gettext("Unexpected response from server")); + } else if ([401, 403, 404, 500].indexOf(response.status) !== -1) { + return Promise.reject(Wirecloud.GlobalLogManager.parseErrorResponse(response)); } - Wirecloud.io.makeRequest(url, { - method: 'PUT', - requestHeaders: {'Accept': 'application/json'}, - contentType: 'application/json', - postBody: JSON.stringify(content), - onComplete: function (response) { - if (response.status === 204) { - this.widgets.filter(function (widget) { - widget.persist(); - }); - resolve(this); - } else { - reject(/* TODO */); - } - }.bind(this) + this.widgets.filter((widget) => { + widget.persist(); }); - }.bind(this)); + return Promise.resolve(this); + }); }; // ========================================================================= @@ -247,14 +247,14 @@ } else { return new Wirecloud.ui.ColumnLayout(this, layoutInfo.columns, layoutInfo.cellheight, layoutInfo.verticalmargin, layoutInfo.horizontalmargin); } - break; case 'gridlayout': return new Wirecloud.ui.GridLayout(this, layoutInfo.columns, layoutInfo.rows, layoutInfo.verticalmargin, layoutInfo.horizontalmargin); } }; /** - * + * TODO, used by WorkspaceTabView to when the user changes the preferences + * for the base layout. */ WorkspaceTabViewDragboard.prototype._updateBaseLayout = function _updateBaseLayout() { // Create the new Layout @@ -274,11 +274,10 @@ if (this.widgets[z] != null) { this.widgets.splice(z, 1, this.widgets[z], widget); this.widgets.forEach(function (value, index) { - if (value != null) { - value.setPosition({ - z: index - }); - } + // forEach skips undefined indexes + value.setPosition({ + z: index + }); }); } else { this.widgets[z] = widget; @@ -297,11 +296,9 @@ this.widgets.splice(z, 1); this.widgets.forEach(function (value, index) { - if (value != null) { - value.setPosition({ - z: index - }); - } + value.setPosition({ + z: index + }); }); this.tab.removeChild(widget); @@ -361,4 +358,4 @@ ns.WorkspaceTabViewDragboard = WorkspaceTabViewDragboard; -})(Wirecloud.ui); +})(Wirecloud.ui, Wirecloud.Utils); From 62d325caa1e3736d22e96cc6162ca3466c93f0d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Arranz?= Date: Fri, 28 Dec 2018 10:40:30 +0100 Subject: [PATCH 04/22] Some fixes --- .../js/wirecloud/UserInterfaceManager.js | 21 +++++++++---------- .../platform/static/js/wirecloud/core.js | 10 ++++++++- .../static/js/wirecloud/ui/WorkspaceView.js | 11 +++++----- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/wirecloud/platform/static/js/wirecloud/UserInterfaceManager.js b/src/wirecloud/platform/static/js/wirecloud/UserInterfaceManager.js index fd5a3d8c6e..d84d05a497 100644 --- a/src/wirecloud/platform/static/js/wirecloud/UserInterfaceManager.js +++ b/src/wirecloud/platform/static/js/wirecloud/UserInterfaceManager.js @@ -166,18 +166,17 @@ return; } - if (state.tab == null || state.tab_id == null || state.workspace_title == null) { - if (state.tab == null) { - state.tab = this.notebook.visibleTab.model.name; - } - if (state.tab_id == null) { - state.tab_id = this.notebook.visibleTab.model.id; - } - if (state.workspace_title == null) { - state.workspace_title = this.model.title; - } - Wirecloud.HistoryManager.replaceState(state); + if (state.tab == null) { + state.tab = this.notebook.visibleTab.model.name; + } + if (state.tab_id == null) { + state.tab_id = this.notebook.visibleTab.model.id; + } + if (state.workspace_title !== this.model.title) { + state.workspace_title = this.model.title; } + Wirecloud.HistoryManager.replaceState(state); + Wirecloud.UserInterfaceManager.header.refresh(); // Handle tab changes this.notebook.addEventListener("changed", function (notebook, oldTab, newTab) { diff --git a/src/wirecloud/platform/static/js/wirecloud/core.js b/src/wirecloud/platform/static/js/wirecloud/core.js index 9bfa7c87a2..87d04a7f59 100644 --- a/src/wirecloud/platform/static/js/wirecloud/core.js +++ b/src/wirecloud/platform/static/js/wirecloud/core.js @@ -198,7 +198,15 @@ Wirecloud.UserInterfaceManager.changeCurrentView('workspace', true); Wirecloud.dispatchEvent('loaded'); - return Wirecloud.changeActiveWorkspace({owner: state.workspace_owner, name: state.workspace_name}, {initialtab: state.tab, history: "replace"}); + return Wirecloud.changeActiveWorkspace( + { + owner: state.workspace_owner, + name: state.workspace_name + }, { + initialtab: state.tab, + history: "replace" + } + ); }, (error) => { var msg = gettext("Error loading WireCloud"); (new Wirecloud.ui.MessageWindowMenu(msg, Wirecloud.constants.LOGGING.ERROR_MSG)).show(); diff --git a/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceView.js b/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceView.js index 23c0b6c677..24b36d79d4 100644 --- a/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceView.js +++ b/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceView.js @@ -194,6 +194,7 @@ this.notebook = new StyledElements.Notebook({ 'class': 'se-notebook-bottom' }); + this.layout.slideOut().content.clear(); this.notebook.appendTo(this.layout.content); loadingTab = this.notebook.createTab(); @@ -320,6 +321,7 @@ return { workspace_owner: currentState.workspace_owner, workspace_name: currentState.workspace_name, + workspace_title: currentState.workspace_title, view: 'workspace' }; }; @@ -351,7 +353,7 @@ { 'label': current_state.workspace_owner }, { - 'label': current_state.workspace_title, + 'label': current_state.workspace_title || current_state.workspace_name, } ]; } else { @@ -411,8 +413,7 @@ alert_msg = document.createElement('div'); alert_msg.className = 'alert alert-info'; alert_msg.textContent = utils.gettext('The requested workspace is no longer available (it was deleted).'); - this.clear(); - this.appendChild(alert_msg); + this.layout.slideOut().content.clear().appendChild(alert_msg); Wirecloud.dispatchEvent('viewcontextchanged'); } else if (Wirecloud.activeWorkspace == null || (nextWorkspace.id !== Wirecloud.activeWorkspace.id)) { Wirecloud.changeActiveWorkspace(nextWorkspace, {initialtab: newState.tab, history: 'ignore'}); @@ -464,9 +465,7 @@ }; var on_workspace_change = function on_workspace_change(workspace) { - var state; - - state = { + var state = { workspace_owner: this.model.owner, workspace_name: this.model.name, workspace_title: this.model.title, From 00315776f1169912a1149ca8a5e8b12786a7e56b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Arranz?= Date: Fri, 28 Dec 2018 10:55:07 +0100 Subject: [PATCH 05/22] Remove not used getNumberOfTabs method from StyledElements.Notebook --- src/wirecloud/commons/static/js/StyledElements/Notebook.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/wirecloud/commons/static/js/StyledElements/Notebook.js b/src/wirecloud/commons/static/js/StyledElements/Notebook.js index 633c0a6222..6829ae5d52 100644 --- a/src/wirecloud/commons/static/js/StyledElements/Notebook.js +++ b/src/wirecloud/commons/static/js/StyledElements/Notebook.js @@ -569,13 +569,6 @@ this.dispatchEvent('changed', oldTab, newTab, options.context); }; - /** - * Devuelve el nĆŗmero de pestaƱas disponibles actualmente en este notebook. - */ - Notebook.prototype.getNumberOfTabs = function getNumberOfTabs() { - return this.tabs.length; - }; - /** * Set the focus on the indicated tab. That is, makes the tab visible on * the tab area of the notebook. From b2fee3898e28aa7880f5a5e5673034a12ae72284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Arranz?= Date: Fri, 28 Dec 2018 11:09:49 +0100 Subject: [PATCH 06/22] Remove getVisibleTab from StyledElements.Notebook --- .../ui/WirecloudCatalogue/ResourceDetailsView.js | 2 +- .../commons/static/js/StyledElements/Notebook.js | 12 ------------ .../platform/static/js/wirecloud/ui/WorkspaceView.js | 2 +- 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/src/wirecloud/catalogue/static/js/wirecloud/ui/WirecloudCatalogue/ResourceDetailsView.js b/src/wirecloud/catalogue/static/js/wirecloud/ui/WirecloudCatalogue/ResourceDetailsView.js index 84a615b89b..6eda2a2a94 100644 --- a/src/wirecloud/catalogue/static/js/wirecloud/ui/WirecloudCatalogue/ResourceDetailsView.js +++ b/src/wirecloud/catalogue/static/js/wirecloud/ui/WirecloudCatalogue/ResourceDetailsView.js @@ -126,7 +126,7 @@ data.resource = this.currentEntry.uri; if (this.currentNotebook) { - data.tab = this.currentNotebook.getVisibleTab().label; + data.tab = this.currentNotebook.visibleTab.label; } } }; diff --git a/src/wirecloud/commons/static/js/StyledElements/Notebook.js b/src/wirecloud/commons/static/js/StyledElements/Notebook.js index 6829ae5d52..30b4c10999 100644 --- a/src/wirecloud/commons/static/js/StyledElements/Notebook.js +++ b/src/wirecloud/commons/static/js/StyledElements/Notebook.js @@ -430,18 +430,6 @@ return null; }; - /** - * Returns current visible tab. - * - * @name StyledElements.Notebook#getVisibleTab - * @deprecated since version 0.5 - * @see {@link StyledElements.Tab#visibleTab} - * @returns {StyledElements.Tab} - */ - Notebook.prototype.getVisibleTab = function getVisibleTab() { - return this.visibleTab; - }; - /** * Returns the tab associated with the given index. * diff --git a/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceView.js b/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceView.js index 24b36d79d4..06df123d37 100644 --- a/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceView.js +++ b/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceView.js @@ -73,7 +73,7 @@ Object.defineProperties(this, { activeTab: { get: function () { - return this.notebook.getVisibleTab(); + return this.notebook.visibleTab; } }, tabs: { From 11632d47345ab7dad044e11d1050ff4e6352266f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Arranz?= Date: Fri, 28 Dec 2018 11:10:53 +0100 Subject: [PATCH 07/22] Fix exitFullscreen tests on StyledElements.Notebook --- src/js_tests/styledelements/NotebookSpec.js | 28 +++++++++++++++------ 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/js_tests/styledelements/NotebookSpec.js b/src/js_tests/styledelements/NotebookSpec.js index a483792c17..289e67f33e 100644 --- a/src/js_tests/styledelements/NotebookSpec.js +++ b/src/js_tests/styledelements/NotebookSpec.js @@ -23,7 +23,7 @@ /* globals StyledElements */ -(function () { +(function (utils) { "use strict"; @@ -412,16 +412,31 @@ }); describe("exitFullscreen()", function () { - var element; + var element, backward; + + beforeAll(() => { + // Check if current browser implements exitFullscreen method + backward = !('exitFullscreen' in document); + }); beforeEach(function () { element = new StyledElements.Notebook(); element.appendTo(dom); + if (backward) { + document.exitFullscreen = jasmine.createSpy("exitFullscreen"); + } else { + spyOn(document, "exitFullscreen"); + } + }); + + afterAll(() => { + if (backward) { + delete document.exitFullscreen; + } }); it("should do nothing if the browser is in fullscreen mode but the notebook is not in fullscreen mode", function () { - document.fullscreenElement = document.createElement('div'); - document.exitFullscreen = jasmine.createSpy('exitFullscreen'); + spyOn(utils, "getFullscreenElement").and.returnValue(document.createElement('div')); expect(element.fullscreen).toBe(false); expect(element.exitFullscreen()).toBe(element); @@ -432,8 +447,7 @@ }); it("should exit from fullscreen if the notebook is in fullscreen mode", function () { - document.fullscreenElement = element.wrapperElement; - document.exitFullscreen = jasmine.createSpy('exitFullscreen'); + spyOn(utils, "getFullscreenElement").and.returnValue(element.wrapperElement); expect(element.fullscreen).toBe(true); expect(element.exitFullscreen()).toBe(element); @@ -538,4 +552,4 @@ }); -})(); +})(StyledElements.Utils); From 6780a3294a5cf22d783be3622a07d5c834a2a9ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Arranz?= Date: Fri, 28 Dec 2018 17:17:03 +0100 Subject: [PATCH 08/22] Complete StyledElement.Notebook coverage --- src/js_tests/styledelements/NotebookSpec.js | 256 +++++++++++++++++- .../static/js/StyledElements/Notebook.js | 27 +- 2 files changed, 252 insertions(+), 31 deletions(-) diff --git a/src/js_tests/styledelements/NotebookSpec.js b/src/js_tests/styledelements/NotebookSpec.js index 289e67f33e..3be41dc2d3 100644 --- a/src/js_tests/styledelements/NotebookSpec.js +++ b/src/js_tests/styledelements/NotebookSpec.js @@ -44,18 +44,42 @@ }); it("should support adding new tabs through a user interface button", function () { - var element, tab, btnCreate; + var element, tabs = []; element = new StyledElements.Notebook(); - element.addEventListener('newTab', function (notebook) {tab = notebook.createTab();}); - btnCreate = element.new_tab_button_tabs; - - btnCreate.click(); - - expect(element.visibleTab).toBe(tab); - expect(element.tabs).toEqual([tab]); - expect(element.tabArea.wrapperElement.children[0]).toBe(tab.getTabElement()); - expect(element.tabArea.wrapperElement.children[1]).toBe(btnCreate.wrapperElement); + element.appendTo(dom); + element.addEventListener('newTab', (notebook) => {tabs.push(notebook.createTab({label: "DynamicTab"}));}); + element.tabArea.style({width: "40px"}); + + // Use the button inside the tab area + element.new_tab_button_tabs.click(); + + expect(element.visibleTab).toBe(tabs[0]); + expect(element.tabs).toEqual(tabs); + expect(element.tabArea.wrapperElement.children[0]).toBe(tabs[0].getTabElement()); + expect(element.tabArea.wrapperElement.children[1]).toBe(element.new_tab_button_tabs.wrapperElement); + expect(element.new_tab_button_tabs.enabled).toBe(false); + expect(element.new_tab_button_left.enabled).toBe(true); + + // Should be possible to register more than one listener + element.addEventListener('newTab', () => {}); + + // Allocate space for a second tab + element.tabArea.style({width: "140px"}); + + // Use the button at the left of the tab area + element.new_tab_button_left.click(); + expect(element.tabs).toEqual(tabs); + expect(element.tabArea.wrapperElement.children[0]).toBe(tabs[0].getTabElement()); + expect(element.tabArea.wrapperElement.children[1]).toBe(tabs[1].getTabElement()); + expect(element.tabArea.wrapperElement.children[2]).toBe(element.new_tab_button_tabs.wrapperElement); + expect(element.new_tab_button_tabs.enabled).toBe(true); + expect(element.new_tab_button_left.enabled).toBe(false); + + // Add a new tab so the space is not enough + element.createTab({name: "another tab"}); + expect(element.new_tab_button_tabs.enabled).toBe(false); + expect(element.new_tab_button_left.enabled).toBe(true); }); it("should provide fullscreen status through the fullscreen property", function () { @@ -73,6 +97,30 @@ expect(element.wrapperElement.id).toBe('myid'); }); + describe("destroy()", () => { + + it("works on empty notebooks", () => { + let element = new StyledElements.Notebook(); + spyOn(element, "remove"); + + expect(element.destroy()).toBe(undefined); + + expect(element.remove).toHaveBeenCalledWith(); + }); + + it("works on non-empty notebooks", () => { + let element = new StyledElements.Notebook(); + element.createTab(); + element.createTab(); + spyOn(element, "remove"); + + expect(element.destroy()).toBe(undefined); + + expect(element.remove).toHaveBeenCalledWith(); + }); + + }); + describe("createTab([options])", function () { var element; @@ -122,46 +170,211 @@ expect(element.tabArea.wrapperElement.children[1]).toBe(tab2.getTabElement()); }); + it("should allow to create new tabs using custom classes", () => { + const MyTab = function MyTab() { + StyledElements.Tab.apply(this, arguments); + }; + utils.inherit(MyTab, StyledElements.Tab); + + let tab = element.createTab({tab_constructor: MyTab}); + + expect(element.tabs).toEqual([tab]); + expect(tab).toEqual(jasmine.any(MyTab)); + }); + + it("throws a TypeError when trying to use an invalid custom class", () => { + const MyTab = function MyTab() {}; + + expect(() => { + element.createTab({tab_constructor: MyTab}); + }).toThrowError(TypeError); + }); + + }); + + describe("enable events", () => { + + var element; + + beforeEach(() => { + element = new StyledElements.Notebook(); + element.appendTo(dom); + element.createTab(); + element.createTab(); + }); + + it("should add a disable layer when disabled", () => { + element.enabled = false; + + expect(element.disabledLayer).not.toEqual(null); + }); + + it("should remove the disable layer when reenabling", () => { + element.enabled = false; + element.enabled = true; + + expect(element.disabledLayer).toEqual(null); + }); + + }); + + // Deprecated + describe("getTab(id)", () => { + + var element, tab; + + beforeEach(() => { + element = new StyledElements.Notebook(); + element.appendTo(dom); + element.createTab(); + tab = element.createTab(); + }); + + it("should return tab", () => { + expect(element.getTab(tab.tabId)).toBe(tab); + }); + + it("should return undefined if there is no tab with the passed id", () => { + expect(element.getTab("10004")).toBe(undefined); + }); + + }); + + // Deprecated + describe("getTabByIndex(index)", () => { + + var element, tab; + + beforeEach(() => { + element = new StyledElements.Notebook(); + element.appendTo(dom); + element.createTab(); + tab = element.createTab(); + }); + + it("should return tab", () => { + expect(element.getTabByIndex(1)).toBe(tab); + }); + + it("should return undefined if there is no tab with the given index", () => { + expect(element.getTabByIndex("10004")).toBe(undefined); + }); + + }); + + describe("getTabIndex(id)", () => { + + var element, tab; + + beforeEach(() => { + element = new StyledElements.Notebook(); + element.appendTo(dom); + element.createTab(); + tab = element.createTab(); + element.createTab(); + }); + + it("should return tab index", () => { + expect(element.getTabIndex(tab.tabId)).toBe(1); + }); + + it("should return null if there is no tab with the passed id", () => { + expect(element.getTabIndex("10004")).toBe(null); + }); + }); describe("goToTab(tab)", function () { - var element, tab1, tab2, tab3; + var element, tab1, tab2, tab3, changelistener, changedlistener; - beforeEach(function () { + beforeEach(() => { element = new StyledElements.Notebook(); element.appendTo(dom); tab1 = element.createTab(); tab2 = element.createTab(); tab3 = element.createTab(); + changelistener = jasmine.createSpy("changelistener"); + changedlistener = jasmine.createSpy("changedlistener"); + element.addEventListener("change", changelistener); + element.addEventListener("changed", changedlistener); + spyOn(element, "focus"); }); it("throws an exception if tab is null", function () { expect(function () {element.goToTab(null);}).toThrow(jasmine.any(TypeError)); + expect(changelistener).not.toHaveBeenCalled(); + expect(changedlistener).not.toHaveBeenCalled(); }); it("throws an exception if tab is not a valid tab id", function () { expect(function () {element.goToTab("mytab4");}).toThrow(jasmine.any(TypeError)); + expect(changelistener).not.toHaveBeenCalled(); + expect(changedlistener).not.toHaveBeenCalled(); }); it("should raise an exception if the passed tab is not owned by the notebook", function () { var other_notebook = new StyledElements.Notebook(); var other_tab = other_notebook.createTab(); expect(function () {element.goToTab(other_tab);}).toThrow(jasmine.any(TypeError)); + expect(changelistener).not.toHaveBeenCalled(); + expect(changedlistener).not.toHaveBeenCalled(); }); - it("does nothing if the passed tab is the visible tab", function () { + it("does focus the tab if the passed tab is the visible tab (focusOnSetVisible: true)", function () { element.goToTab(tab1); + expect(element.tabArea.wrapperElement.children[2]).toBe(tab3.getTabElement()); + expect(changelistener).not.toHaveBeenCalled(); + expect(changedlistener).not.toHaveBeenCalled(); + expect(element.focus).toHaveBeenCalledWith(tab1.tabId); }); it("should allow to move to a middle tab", function () { element.goToTab(tab2); expect(element.visibleTab).toBe(tab2); + expect(changelistener).toHaveBeenCalledWith(element, tab1, tab2); + expect(changedlistener).toHaveBeenCalledWith(element, tab1, tab2); + expect(element.focus).toHaveBeenCalledWith(tab2.tabId); }); it("should allow to move to the last tab", function () { element.goToTab(tab3); expect(element.visibleTab).toBe(tab3); + expect(changelistener).toHaveBeenCalledWith(element, tab1, tab3); + expect(changedlistener).toHaveBeenCalledWith(element, tab1, tab3); + expect(element.focus).toHaveBeenCalledWith(tab3.tabId); + }); + + it("does nothing if the passed tab is the visible tab (focusOnSetVisible: false)", () => { + element = new StyledElements.Notebook({focusOnSetVisible: false}); + element.appendTo(dom); + tab1 = element.createTab(); + element.addEventListener("change", changelistener); + element.addEventListener("changed", changedlistener); + spyOn(element, "focus"); + + element.goToTab(tab1); + + expect(changelistener).not.toHaveBeenCalled(); + expect(changedlistener).not.toHaveBeenCalled(); + expect(element.focus).not.toHaveBeenCalled(); + }); + + it("should no focus tabs when focusOnSetVisible is false", () => { + element = new StyledElements.Notebook({focusOnSetVisible: false}); + element.appendTo(dom); + tab1 = element.createTab(); + tab2 = element.createTab(); + element.addEventListener("change", changelistener); + element.addEventListener("changed", changedlistener); + spyOn(element, "focus"); + + element.goToTab(tab2); + + expect(element.visibleTab).toBe(tab2); + expect(changelistener).toHaveBeenCalledWith(element, tab1, tab2); + expect(changedlistener).toHaveBeenCalledWith(element, tab1, tab2); + expect(element.focus).not.toHaveBeenCalled(); }); }); @@ -196,7 +409,7 @@ }); it("should allow removing tabs by id", function () { - expect(element.removeTab(tab2.getId())).toBe(element); + expect(element.removeTab(tab2.tabId)).toBe(element); expect(element.tabs).toEqual([tab1, tab3]); expect(element.tabArea.wrapperElement.children[0]).toBe(tab1.getTabElement()); @@ -392,6 +605,21 @@ done(); }); }); + + it("should do nothing if the tab to focus is removed before processing the focus command", (done) => { + element.appendTo(dom); + var tab1 = element.createTab({name: "Tab 1"}); + var tab2 = element.createTab({name: "mytab"}); + element.focus(tab2); + var p = element.focus(tab1); + element.removeTab(tab1); + + p.then(() => { + // this number depends on CSS + expect(dom.querySelector('.se-notebook-tab-area').scrollLeft).toBe(0); + done(); + }); + }); }); describe("requestFullscreen()", function () { diff --git a/src/wirecloud/commons/static/js/StyledElements/Notebook.js b/src/wirecloud/commons/static/js/StyledElements/Notebook.js index 30b4c10999..5e4463ae8f 100644 --- a/src/wirecloud/commons/static/js/StyledElements/Notebook.js +++ b/src/wirecloud/commons/static/js/StyledElements/Notebook.js @@ -280,6 +280,7 @@ this.moveRightButton.setDisabled(last_tab_visible); if (this.new_tab_button_tabs != null) { + first_tab_visible = isTabVisible.call(this, 0, true); if (first_tab_visible && last_tab_visible) { this.new_tab_button_tabs.enable(); this.new_tab_button_left.disable(); @@ -292,13 +293,7 @@ }; var getFirstVisibleTab = function getFirstVisibleTab() { - var i; - for (i = 0; i < this.tabs.length; i += 1) { - if (isTabVisible.call(this, i)) { - return i; - } - } - return null; + return this.tabs.findIndex((element, index) => {return isTabVisible.call(this, index);}); }; /** @@ -399,6 +394,7 @@ * Returns the tab associated with the given ids. * * @since 0.5 + * @deprecated since version 1.0 * @name StyledElements.Notebook#getTab * * @param {Object} id @@ -434,6 +430,7 @@ * Returns the tab associated with the given index. * * @since 0.5 + * @deprecated since version 1.0 * @name StyledElements.Notebook#getTabByIndex * @param {Number} index index of the tab to recover. * @@ -514,7 +511,7 @@ * * @param {Number|Tab} tab intance or tab id of the tab to make visible */ - Notebook.prototype.goToTab = function goToTab(tab, options) { + Notebook.prototype.goToTab = function goToTab(tab) { var newTab, oldTab; if (tab instanceof StyledElements.Tab) { @@ -530,10 +527,6 @@ } oldTab = this.visibleTab; - if (options == null) { - options = {}; - } - if (this.visibleTab && newTab === this.visibleTab) { if (this.focusOnSetVisible) { this.focus(newTab.tabId); @@ -541,11 +534,11 @@ return; } - this.dispatchEvent('change', oldTab, newTab, options.context); + this.dispatchEvent('change', oldTab, newTab); - if (this.visibleTab) { - this.visibleTab.setVisible(false); - } + // At this point there is always a visibleTab + // if (this.visibleTab) { + this.visibleTab.setVisible(false); this.visibleTab = newTab; this.visibleTab.setVisible(true); @@ -554,7 +547,7 @@ this.focus(newTab.tabId); } - this.dispatchEvent('changed', oldTab, newTab, options.context); + this.dispatchEvent('changed', oldTab, newTab); }; /** From 2d6cf827aa1203428c6a0d1c20feb57d7409d200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Arranz?= Date: Fri, 28 Dec 2018 17:22:50 +0100 Subject: [PATCH 09/22] Remove deprecated getId method from StyledElement.Notebook --- .../commons/static/js/StyledElements/Notebook.js | 2 +- .../commons/static/js/StyledElements/Tab.js | 14 -------------- .../src/wirecloud-components-showcase/code.js | 2 +- 3 files changed, 2 insertions(+), 16 deletions(-) diff --git a/src/wirecloud/commons/static/js/StyledElements/Notebook.js b/src/wirecloud/commons/static/js/StyledElements/Notebook.js index 5e4463ae8f..bfacaeb936 100644 --- a/src/wirecloud/commons/static/js/StyledElements/Notebook.js +++ b/src/wirecloud/commons/static/js/StyledElements/Notebook.js @@ -467,7 +467,7 @@ if (this.tabsById[tab.tabId] !== tab) { throw new TypeError('tab is not owned by this notebook'); } - tab = tab.getId(); + tab = tab.tabId; } if (!this.tabsById[tab]) { diff --git a/src/wirecloud/commons/static/js/StyledElements/Tab.js b/src/wirecloud/commons/static/js/StyledElements/Tab.js index 14d68e938f..259b4b0fc0 100644 --- a/src/wirecloud/commons/static/js/StyledElements/Tab.js +++ b/src/wirecloud/commons/static/js/StyledElements/Tab.js @@ -239,20 +239,6 @@ return this; }; - /** - * Returns the id of this Tab. - * - * @name StyledElements.Tab#getId - * @deprecated since version 0.5 - * @see {@link StyledElements.Tab#tabId} - * - * @returns {String} - * id of the tab - */ - Tab.prototype.getId = function getId() { - return this.tabId; - }; - /** * TODO change this. * diff --git a/src/wirecloud/commons/test-data/src/wirecloud-components-showcase/code.js b/src/wirecloud/commons/test-data/src/wirecloud-components-showcase/code.js index d90e30bc5b..8a0a46b3c3 100644 --- a/src/wirecloud/commons/test-data/src/wirecloud-components-showcase/code.js +++ b/src/wirecloud/commons/test-data/src/wirecloud-components-showcase/code.js @@ -338,7 +338,7 @@ var init = function init() { tab.appendChild(document.createTextNode('Tab created dinamically'));\n\ notebook.goToTab(tab);\n\ });\n\ - goToTab3Button.addEventListener('click', function() {notebook.goToTab(tab3.getId())});\n\ + goToTab3Button.addEventListener('click', function() {notebook.goToTab(tab3)};);\n\ \n"; insertExample("Notebook", code); From 2184068201f9ea48d0aaa4db72b792b50b10d277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Arranz?= Date: Fri, 28 Dec 2018 17:27:01 +0100 Subject: [PATCH 10/22] Remove deprecated getId method from StyledElement.Alternative --- src/js_tests/styledelements/AlternativesSpec.js | 4 ++-- src/wirecloud/commons/static/js/StyledElements/Alternative.js | 4 ---- .../test-data/src/wirecloud-components-showcase/code.js | 4 ++-- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/js_tests/styledelements/AlternativesSpec.js b/src/js_tests/styledelements/AlternativesSpec.js index dfe998cca6..f2dcef232b 100644 --- a/src/js_tests/styledelements/AlternativesSpec.js +++ b/src/js_tests/styledelements/AlternativesSpec.js @@ -181,7 +181,7 @@ expect(element.wrapperElement.children[1]).toBe(alt3.wrapperElement); }); - var p = element.removeAlternative(alt2.getId(), {onComplete: listener}); + var p = element.removeAlternative(alt2.altId, {onComplete: listener}); expect(element.alternativeList).toEqual([alt1, alt3]); expect(p).toEqual(jasmine.any(Promise)); @@ -367,7 +367,7 @@ expect(element.visibleAlt).toBe(alt2); }); - var p = element.showAlternative(alt2.getId(), { + var p = element.showAlternative(alt2.altId, { onComplete: listener }); diff --git a/src/wirecloud/commons/static/js/StyledElements/Alternative.js b/src/wirecloud/commons/static/js/StyledElements/Alternative.js index 761d1d336f..e355f6600c 100644 --- a/src/wirecloud/commons/static/js/StyledElements/Alternative.js +++ b/src/wirecloud/commons/static/js/StyledElements/Alternative.js @@ -71,10 +71,6 @@ return !this.hidden; }; - Alternative.prototype.getId = function getId() { - return this.altId; - }; - se.Alternative = Alternative; })(StyledElements, StyledElements.Utils); diff --git a/src/wirecloud/commons/test-data/src/wirecloud-components-showcase/code.js b/src/wirecloud/commons/test-data/src/wirecloud-components-showcase/code.js index 8a0a46b3c3..474b2307bd 100644 --- a/src/wirecloud/commons/test-data/src/wirecloud-components-showcase/code.js +++ b/src/wirecloud/commons/test-data/src/wirecloud-components-showcase/code.js @@ -44,7 +44,7 @@ var init = function init() { var alternative = alternatives.createAlternative(); alternative.appendChild(panelNotebook); - list.addEntries([[alternative.getId(), name]]); + list.addEntries([[alternative.altId, name]]); var preText = document.createElement("pre"); var codeTab = panelNotebook.createTab({name: "Code", closable: false}); @@ -66,7 +66,7 @@ var init = function init() { var alternative = alternatives.createAlternative(); alternative.appendChild(panelNotebook); - list.addEntries([[alternative.getId(), name]]); + list.addEntries([[alternative.altId, name]]); var preText = document.createElement("pre"); var codeTab = panelNotebook.createTab({name: "HTML", closable: false}); From 021c01bc928eaf8f0979ed3ddb2f355c6c55804f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Arranz?= Date: Fri, 28 Dec 2018 17:47:35 +0100 Subject: [PATCH 11/22] Add edit mode support --- .../ui/WorkspaceTabViewDragboardSpec.js | 29 +++++++++++ .../wirecloud/ui/WorkspaceViewSpec.js | 44 ++++++++++++++-- .../static/css/workspace/widget.scss | 10 ++++ .../wirecloud/ui/WorkspaceTabViewDragboard.js | 7 +++ .../static/js/wirecloud/ui/WorkspaceView.js | 51 ++++++++++++++++--- 5 files changed, 129 insertions(+), 12 deletions(-) diff --git a/src/js_tests/wirecloud/ui/WorkspaceTabViewDragboardSpec.js b/src/js_tests/wirecloud/ui/WorkspaceTabViewDragboardSpec.js index b6bce0c337..16c8bed904 100644 --- a/src/js_tests/wirecloud/ui/WorkspaceTabViewDragboardSpec.js +++ b/src/js_tests/wirecloud/ui/WorkspaceTabViewDragboardSpec.js @@ -40,6 +40,7 @@ }, removeChild: jasmine.createSpy("removeChild"), workspace: { + editing: true, model: { id: 8 }, @@ -591,6 +592,34 @@ }; }); + it("should do nothing when no passing the ids parameter and not editing", (done) => { + spyOn(Wirecloud.io, "makeRequest"); + let tab = create_tab(); + tab.workspace.editing = false; + dragboard = new ns.WorkspaceTabViewDragboard(tab); + + let p = dragboard.update(); + + p.then(() => { + expect(Wirecloud.io.makeRequest).not.toHaveBeenCalled(); + done(); + }, fail); + }); + + it("should do nothing when passing the ids parameter and not editing", (done) => { + spyOn(Wirecloud.io, "makeRequest"); + let tab = create_tab(); + tab.workspace.editing = false; + dragboard = new ns.WorkspaceTabViewDragboard(tab); + + let p = dragboard.update(["1"]); + + p.then(() => { + expect(Wirecloud.io.makeRequest).not.toHaveBeenCalled(); + done(); + }, fail); + }); + it("should persist all widgets when no passing the ids parameter", (done) => { spyOn(Wirecloud.io, "makeRequest").and.callFake((url, options) => { return new Wirecloud.Task("Sending request", (resolve) => {resolve({status: 204});}); diff --git a/src/js_tests/wirecloud/ui/WorkspaceViewSpec.js b/src/js_tests/wirecloud/ui/WorkspaceViewSpec.js index 969f3683f6..3031b91e03 100644 --- a/src/js_tests/wirecloud/ui/WorkspaceViewSpec.js +++ b/src/js_tests/wirecloud/ui/WorkspaceViewSpec.js @@ -129,6 +129,7 @@ expect(ns.ComponentSidebar).toHaveBeenCalledWith(); expect(view.view_name).toBe("workspace"); + expect(view.editing).toBe(false); }); it("should allow to change the activeWorkspace through the workspace menu", () => { @@ -424,21 +425,40 @@ beforeEach(() => { view = new ns.WorkspaceView(1); Wirecloud.dispatchEvent('loaded'); - let workspace = create_workspace(); - view.loadWorkspace(workspace); }); it("should return null if the widget is not present", () => { + let workspace = create_workspace(); + view.loadWorkspace(workspace); expect(view.findWidget("1")).toBe(null); }); it("should return a widget if present", () => { + let workspace = create_workspace(); + view.loadWorkspace(workspace); let widget = {}; view.tabs[0].findWidget.and.returnValue(widget); expect(view.findWidget("8")).toBe(widget); }); + it("should work while loading", () => { + // findWidget can be called while loading, in this case + // WorkspaceView will have a temporal tab to display the + // loading animation, this tab should be skipped + spyOn(StyledElements.Notebook.prototype, "goToTab").and.callFake(function () { + let widget = {}; + view.tabs[1].findWidget.and.returnValue(widget); + + expect(view.findWidget("8")).toBe(widget); + this.visibleTab = view.tabs[1]; + }); + + let workspace = create_workspace(); + view.loadWorkspace(workspace); + expect(view.notebook.goToTab).toHaveBeenCalled(); + }); + }); describe("getBreadcrumb()", () => { @@ -535,6 +555,7 @@ jasmine.any(StyledElements.Button), jasmine.any(StyledElements.Button), jasmine.any(StyledElements.Button), + jasmine.any(StyledElements.Button), jasmine.any(StyledElements.Button) ]); }); @@ -553,18 +574,18 @@ jasmine.any(StyledElements.Button), jasmine.any(StyledElements.Button), jasmine.any(StyledElements.Button), + jasmine.any(StyledElements.Button), jasmine.any(StyledElements.Button) ]); - buttons[1].click(); - buttons[2].click(); + buttons[2].enable().click(); buttons[3].click(); + buttons[4].click(); expect(Wirecloud.UserInterfaceManager.changeCurrentView).toHaveBeenCalledWith("wiring"); expect(Wirecloud.UserInterfaceManager.changeCurrentView).toHaveBeenCalledWith("myresources"); expect(Wirecloud.UserInterfaceManager.changeCurrentView).toHaveBeenCalledWith("marketplace"); expect(Wirecloud.UserInterfaceManager.changeCurrentView.calls.count()).toBe(3); - }); }); @@ -865,6 +886,19 @@ callEventListener(workspace, "unload"); expect(view.model).toBe(null); + expect(view.editing).toBe(false); + }); + + it("should disable edit mode on unload events", () => { + let workspace = create_workspace(); + workspace.isAllowed.and.returnValue(true); + view.loadWorkspace(workspace); + view.editButton.click(); + + callEventListener(workspace, "unload"); + + expect(view.model).toBe(null); + expect(view.editing).toBe(false); }); }); diff --git a/src/wirecloud/defaulttheme/static/css/workspace/widget.scss b/src/wirecloud/defaulttheme/static/css/workspace/widget.scss index ee94298c3f..694e635f5c 100644 --- a/src/wirecloud/defaulttheme/static/css/workspace/widget.scss +++ b/src/wirecloud/defaulttheme/static/css/workspace/widget.scss @@ -33,6 +33,11 @@ cursor: grab; position: relative; flex-grow: 0; + display: none; + + .wc-workspace-editing & { + display: block; + } & > span { display: inline-block; @@ -97,6 +102,7 @@ padding: 0 32px; position: static; flex-grow: 0; + display: none; @if ($widget-footer-bg == 'transparent') { color: contrast-color($body-bg, $text-color-light, $text-color-dark, $text-color-threshold); @@ -104,6 +110,10 @@ color: contrast-color($widget-footer-bg, $text-color-light, $text-color-dark, $text-color-threshold); background: $widget-footer-bg; } + + .wc-workspace-editing & { + display: block; + } } .wc-widget .wc-bottom-resize-handle { diff --git a/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceTabViewDragboard.js b/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceTabViewDragboard.js index b08c882310..127469b222 100644 --- a/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceTabViewDragboard.js +++ b/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceTabViewDragboard.js @@ -182,7 +182,14 @@ this.painted = true; }; + /** + * + */ WorkspaceTabViewDragboard.prototype.update = function update(ids) { + if (this.tab.workspace.editing === false) { + return Promise.resolve(this); + } + var url = Wirecloud.URLs.IWIDGET_COLLECTION.evaluate({ workspace_id: this.tab.workspace.model.id, tab_id: this.tab.model.id diff --git a/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceView.js b/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceView.js index 06df123d37..298a4dbeb2 100644 --- a/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceView.js +++ b/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceView.js @@ -38,6 +38,14 @@ this.wsMenu.appendSeparator(); this.wsMenu.append(new Wirecloud.ui.WorkspaceViewMenuItems(this)); + this.editButton = new se.ToggleButton({ + class: "wc-edit-mode-button", + iconClass: "fa fa-pencil" + }); + this.editButton.addEventListener("click", (button) => { + showHideTabBar.call(this, button.active); + }); + this.walletButton = this.buildAddWidgetButton(); this.wiringButton = new StyledElements.Button({ @@ -71,6 +79,11 @@ this.appendChild(this.layout); Object.defineProperties(this, { + editing: { + get: () => { + return this.editButton.active; + } + }, activeTab: { get: function () { return this.notebook.visibleTab; @@ -168,6 +181,9 @@ var i, widget; for (i = 0; i < this.notebook.tabs.length; i++) { + if (!(this.notebook.tabs[i] instanceof ns.WorkspaceTabView)) { + continue; + } widget = this.notebook.tabs[i].findWidget(id); if (widget != null) { return widget; @@ -241,14 +257,18 @@ this.notebook.removeTab(loadingTab); if (this.model.isAllowed('edit')) { - var button = new StyledElements.Button({ + this.addTabButton = new StyledElements.Button({ title: utils.gettext("New tab"), iconClass: "fa fa-plus", class: "wc-create-workspace-tab" }); - this.notebook.addButton(button); - button.addEventListener('click', on_click_createtab.bind(this)); + this.notebook.addButton(this.addTabButton); + this.addTabButton.addEventListener('click', on_click_createtab.bind(this)); + } else { + this.addTabButton = null; } + this.editButton.enabled = this.model.isAllowed('edit'); + this.editButton.active = false; if (Wirecloud.Utils.isFullscreenSupported()) { this.fullscreenButton = new StyledElements.Button({ @@ -295,6 +315,7 @@ window.open('http://conwet.fi.upm.es/wirecloud/', '_blank'); }); } + showHideTabBar.call(this, false); }; WorkspaceView.prototype.buildAddWidgetButton = function buildAddWidgetButton() { @@ -353,7 +374,7 @@ { 'label': current_state.workspace_owner }, { - 'label': current_state.workspace_title || current_state.workspace_name, + 'label': current_state.workspace_title, } ]; } else { @@ -390,9 +411,8 @@ WorkspaceView.prototype.getToolbarButtons = function getToolbarButtons() { if (Wirecloud.contextManager && Wirecloud.contextManager.get('username') !== 'anonymous') { - this.walletButton.enabled = this.model != null && this.model.isAllowed('edit'); - this.wiringButton.enabled = this.model != null && this.model.isAllowed('edit'); - return [this.walletButton, this.wiringButton, this.myresourcesButton, this.marketButton]; + + return [this.editButton, this.walletButton, this.wiringButton, this.myresourcesButton, this.marketButton]; } else { return []; } @@ -456,6 +476,17 @@ // EVENT HANDLERS // ========================================================================= + const showHideTabBar = function showHideTabBar(editing) { + this.layout.content.toggleClassName("wc-workspace-editing", editing); + + this.walletButton.enabled = editing && this.model.isAllowed('edit'); + this.wiringButton.enabled = editing && this.model.isAllowed('edit'); + this.notebook.tabWrapper.toggleClassName("hidden", !(editing || this.tabs.length > 1)); + if (this.addTabButton) { + this.addTabButton.toggleClassName("hidden", !editing); + } + }; + const on_workspace_createoperator = function on_workspace_createoperator(workspace_model, operator) { this.layout.content.appendChild(operator.wrapperElement); }; @@ -487,6 +518,11 @@ var on_workspace_unload = function on_workspace_unload(workspace) { // This must be always the case // if (this.model === workspace) { + this.model = null; + this.editButton.enabled = false; + this.editButton.active = false; + this.walletButton.enabled = false; + this.wiringButton.enabled = false; this.layout.content.clear(); workspace.removeEventListener('remove', this.on_workspace_remove_bound); workspace.removeEventListener('change', this.on_workspace_change_bound); @@ -494,6 +530,7 @@ workspace.removeEventListener('createoperator', this.on_workspace_createoperator_bound); workspace.removeEventListener('removeoperator', this.on_workspace_removeoperator_bound); this.model = null; + showHideTabBar.call(this, false); }; var on_click_createtab = function on_click_createtab(button) { From acb75c69774aa9405eef486d216f4cedaf87a5d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Arranz?= Date: Sun, 13 Jan 2019 23:44:24 +0100 Subject: [PATCH 12/22] StyledElements.Alternatives: Provide a value when fulfilling promises --- .../styledelements/AlternativesSpec.js | 18 +++++++++++++++--- .../static/js/StyledElements/Alternatives.js | 14 ++++++++------ 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/js_tests/styledelements/AlternativesSpec.js b/src/js_tests/styledelements/AlternativesSpec.js index f2dcef232b..cd288d79f4 100644 --- a/src/js_tests/styledelements/AlternativesSpec.js +++ b/src/js_tests/styledelements/AlternativesSpec.js @@ -342,7 +342,11 @@ var p = element.showAlternative(alt1); expect(p).toEqual(jasmine.any(Promise)); - p.then(() => { + p.then((result) => { + expect(result).toEqual({ + in: alt1, + out: alt1 + }); expect(element.visibleAlt).toBe(alt1); done(); }); @@ -356,7 +360,11 @@ }); expect(p).toEqual(jasmine.any(Promise)); - p.then(() => { + p.then((result) => { + expect(result).toEqual({ + in: alt3, + out: alt1 + }); expect(element.visibleAlt).toBe(alt3); done(); }); @@ -372,7 +380,11 @@ }); expect(p).toEqual(jasmine.any(Promise)); - p.then(() => { + p.then((result) => { + expect(result).toEqual({ + in: alt2, + out: alt1 + }); expect(listener).toHaveBeenCalledWith(element, alt1, alt2); done(); }); diff --git a/src/wirecloud/commons/static/js/StyledElements/Alternatives.js b/src/wirecloud/commons/static/js/StyledElements/Alternatives.js index 7f978715ea..d1c2298c6d 100644 --- a/src/wirecloud/commons/static/js/StyledElements/Alternatives.js +++ b/src/wirecloud/commons/static/js/StyledElements/Alternatives.js @@ -87,7 +87,7 @@ if (inAlternative === outAlternative) { utils.callCallback(command.onComplete, context, outAlternative, inAlternative); - return false; // we are not going to process this command + return {in: inAlternative, out: outAlternative}; // we are not going to process this command } p = build_transit_promise(command.effect, outAlternative, inAlternative, context); @@ -110,9 +110,10 @@ }.bind(this)); } - return p.then(function () { + return p.then((result) => { // Call the onComplete callback utils.callCallback(command.onComplete, context, outAlternative, inAlternative); + return result; }); }; @@ -320,7 +321,7 @@ }; var build_transit_promise = function build_transit_promise(effect, outAlternative, inAlternative, context) { - var p = new Promise(function (fulfill) { + var p = new Promise((fulfill) => { // Throw an event notifying we are going to change the visible alternative context.dispatchEvent('preTransition', outAlternative, inAlternative); context.wrapperElement.classList.add('se-on-transition'); @@ -362,24 +363,25 @@ outAlternative.removeClassName('fade').hide(); }); // Trigger fade effects - setTimeout(function () { + setTimeout(() => { inAlternative.addClassName('in'); outAlternative.removeClassName('in'); }, 10); break; default: case StyledElements.Alternatives.NONE: - p = p.then(function () { + p = p.then(() => { inAlternative.show(); outAlternative.hide(); }); } - return p.then(function () { + return p.then(() => { privates.get(context).visibleAlt = inAlternative; context.wrapperElement.classList.remove('se-on-transition'); // Throw an event notifying we have changed the visible alternative context.dispatchEvent('postTransition', outAlternative, inAlternative); + return {in: inAlternative, out: outAlternative}; }); }; From 186cf7e51540857eb9fad0da9a0e6da860330630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Arranz?= Date: Mon, 14 Jan 2019 00:10:12 +0100 Subject: [PATCH 13/22] Fix StyledElements.Utils.waitTransition for display: none elements --- src/js_tests/styledelements/UtilsSpec.js | 33 +++++++++++++++++++ .../commons/static/js/StyledElements/Utils.js | 18 ++++++---- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/src/js_tests/styledelements/UtilsSpec.js b/src/js_tests/styledelements/UtilsSpec.js index 8b978c3a05..440435bf49 100644 --- a/src/js_tests/styledelements/UtilsSpec.js +++ b/src/js_tests/styledelements/UtilsSpec.js @@ -482,6 +482,39 @@ }); }); + describe("waitTransition(element)", () => { + var waitTransition; + + beforeAll(() => { + waitTransition = StyledElements.Utils.waitTransition; + }); + + it("should immediatelly resolve for display: none elements", (done) => { + let element = document.createElement("div"); + element.style.display = "none"; + + waitTransition(element).then(done, fail); + }); + + it("should wait transitionend events on elements", (done) => { + let listener = jasmine.createSpy("listener"); + let element = document.createElement("div"); + + waitTransition(element).then(listener, fail); + + setTimeout(() => { + expect(listener).not.toHaveBeenCalled(); + + element.dispatchEvent(new TransitionEvent("transitionend")); + + setTimeout(() => { + expect(listener).toHaveBeenCalled(); + done(); + }, 0); + }, 0); + }); + }); + describe("values(object)", function () { var values; diff --git a/src/wirecloud/commons/static/js/StyledElements/Utils.js b/src/wirecloud/commons/static/js/StyledElements/Utils.js index 00f0cda93d..0bf04991ee 100644 --- a/src/wirecloud/commons/static/js/StyledElements/Utils.js +++ b/src/wirecloud/commons/static/js/StyledElements/Utils.js @@ -945,13 +945,19 @@ if (window.StyledElements == null) { }; Utils.waitTransition = function waitTransition(element) { - return new Promise(function (fulfill) { - var listener = function listener(event) { - element.removeEventListener('transitionend', listener); + return new Promise((fulfill) => { + let w = element.ownerDocument.defaultView; + let display = w.getComputedStyle(element, null).getPropertyValue("display"); + if (display !== "none") { + let listener = function listener(event) { + element.removeEventListener('transitionend', listener); + fulfill(); + }; + + element.addEventListener('transitionend', listener); + } else { fulfill(); - }; - - element.addEventListener('transitionend', listener); + } }); }; From 69ee3ab3b8089cb47ea56706c2f44fb3e57e6bf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Arranz?= Date: Mon, 14 Jan 2019 00:13:24 +0100 Subject: [PATCH 14/22] Fix selenium tests --- src/js_tests/wirecloud/WidgetSpec.js | 34 +- src/wirecloud/commons/utils/remote.py | 69 +- .../platform/localcatalogue/tests.py | 11 +- .../js/wirecloud/UserInterfaceManager.js | 11 +- .../platform/static/js/wirecloud/Widget.js | 13 +- .../wirecloud/Widget/PreferencesWindowMenu.js | 2 + .../static/js/wirecloud/ui/MarketplaceView.js | 8 +- .../js/wirecloud/ui/PreferencesWindowMenu.js | 1 + .../static/js/wirecloud/ui/WorkspaceView.js | 5 + .../js/wirecloud/ui/WorkspaceViewMenuItems.js | 4 +- src/wirecloud/platform/tests/selenium.py | 850 +++++++++--------- src/wirecloud/platform/wiring/tests.py | 825 +++++++++-------- 12 files changed, 993 insertions(+), 840 deletions(-) diff --git a/src/js_tests/wirecloud/WidgetSpec.js b/src/js_tests/wirecloud/WidgetSpec.js index d81423ab7a..a567333597 100644 --- a/src/js_tests/wirecloud/WidgetSpec.js +++ b/src/js_tests/wirecloud/WidgetSpec.js @@ -881,22 +881,46 @@ describe("reload()", () => { - it("should reload widget view", () => { + it("should reload widget view", (done) => { var widget = new Wirecloud.Widget(WORKSPACE_TAB, EMPTY_WIDGET_META, { id: "1", title: "old title" }); + let listener = jasmine.createSpy("listener"); + let element = widget.wrapperElement; widget.wrapperElement = { + contentDocument: { + defaultView: { + addEventListener: jasmine.createSpy("addEventListener") + } + }, contentWindow: { location: { - reload: jasmine.createSpy("reload") + href: widget.codeurl, + reload: jasmine.createSpy("reload").and.callFake(() => { + // call unload event + widget.wrapperElement.contentDocument.defaultView.addEventListener.calls.argsFor(0)[1](); + }), + replace: jasmine.createSpy("replace") } }, setAttribute: jasmine.createSpy("setAttribute") }; - expect(widget.reload()).toBe(widget); - - expect(widget.wrapperElement.contentWindow.location.reload).toHaveBeenCalledWith(); + widget.addEventListener("unload", listener); + widget.addEventListener("load", () => { + setTimeout(() => { + expect(listener).not.toHaveBeenCalled(); + expect(widget.reload()).toBe(widget); + expect(widget.wrapperElement.contentWindow.location.reload).toHaveBeenCalledWith(); + + setTimeout(() => { + expect(listener).toHaveBeenCalledTimes(1); + done(); + }, 0); + }, 0); + }); + widget.load(); + element.dispatchEvent(new Event("load")); }); }); diff --git a/src/wirecloud/commons/utils/remote.py b/src/wirecloud/commons/utils/remote.py index f5458e1fc9..0d28221731 100644 --- a/src/wirecloud/commons/utils/remote.py +++ b/src/wirecloud/commons/utils/remote.py @@ -299,6 +299,10 @@ def has_icon(self, extra_class): class FieldTester(WebElementTester): + @property + def is_disabled(self): + return self.get_attribute('disabled').strip().lower() == "true" + @property def is_selected(self): return self.element.is_selected() @@ -474,9 +478,10 @@ def tab_created(driver): return WebDriverWait(self.driver, timeout=5).until(tab_created) def create_widget(self, query, new_title=None, version=None): - with self.resource_sidebar as sidebar: - resource = sidebar.search_component('widget', query) - tab_widget = resource.create_component(version=version) + with self.edit_mode as edit_session: + with edit_session.resource_sidebar as sidebar: + resource = sidebar.search_component('widget', query) + tab_widget = resource.create_component(version=version) if new_title is not None: tab_widget.rename(new_title) @@ -496,6 +501,34 @@ def find_widget(self, id=None, title=None): return None +class EditModeSession(object): + + def __init__(self, testcase): + self.resource_sidebar = WorkspaceComponentSidebarTester(testcase) + self.wiring_view = WiringViewTester(testcase) + + +class EditMode(object): + + def __init__(self, testcase): + self.testcase = testcase + self.nestinglevel = 0 + + def __enter__(self): + if self.nestinglevel == 0: + edit_mode_button = self.testcase.find_navbar_button("wc-edit-mode-button") + edit_mode_button.click() + self.nestinglevel += 1 + + return EditModeSession(self.testcase) + + def __exit__(self, type, value, traceback): + self.nestinglevel -= 1 + if self.nestinglevel == 0: + edit_mode_button = self.testcase.find_navbar_button("wc-edit-mode-button") + edit_mode_button.click() + + class WorkspaceComponentSidebarTester(object): def __init__(self, testcase): @@ -824,6 +857,7 @@ def minimize(self, timeout=10): def reload(self): self.open_menu().click_entry('Reload') + return self def remove(self, timeout=10): old_length = len(self.testcase.driver.find_elements_by_css_selector(".wc-workspace .wc-widget")) @@ -1287,10 +1321,9 @@ def tearDownClass(cls): def setUp(self): - self.resource_sidebar = WorkspaceComponentSidebarTester(self) + self.edit_mode = EditMode(self) self.marketplace_view = MarketplaceViewTester(self) self.myresources_view = MyResourcesViewTester(self) - self.wiring_view = WiringViewTester(self) def tearDown(self): @@ -1333,21 +1366,29 @@ def wait_wirecloud_unload(self, timeout=15): loading_window = self.wait_element_visible('#loading-window') WebDriverWait(self.driver, timeout).until(EC.staleness_of(loading_window)) - def wait_wirecloud_ready(self, start_timeout=20, timeout=20, embedded=False): + def wait_wirecloud_ready(self, start_timeout=20, timeout=20, login=False, embedded=False): loading_window = None def wait_loading_window_fadding(driver): return 'in' not in loading_window.get_attribute('class').strip() - loading_window = self.wait_element_visible('#loading-window') - WebDriverWait(self.driver, timeout).until(wait_loading_window_fadding) - - loading_message = loading_window.find_element_by_id('loading-message') try: - self.driver.execute_script("arguments[0].click();", loading_message) - except: - pass + loading_window = self.wait_element_visible('#loading-window') + except TimeoutException: + # On page load, selenium sometimes waits until the loading process + # ends completely. In that case, the loading window element won't be + # visible, but because WireCloud is already ready. + if not login: + raise + else: + WebDriverWait(self.driver, timeout).until(wait_loading_window_fadding) + + loading_message = loading_window.find_element_by_id('loading-message') + try: + self.driver.execute_script("arguments[0].click();", loading_message) + except: + pass if embedded: self.wait_element_visible('.wc-body:not(.se-on-transition)') @@ -1372,7 +1413,7 @@ def login(self, username='admin', password='admin', next=None): form.get_field('password').set_value(password) form.submit() - self.wait_wirecloud_ready() + self.wait_wirecloud_ready(login=True) def get_current_view(self): diff --git a/src/wirecloud/platform/localcatalogue/tests.py b/src/wirecloud/platform/localcatalogue/tests.py index 0765d1533a..9d9558c4d6 100644 --- a/src/wirecloud/platform/localcatalogue/tests.py +++ b/src/wirecloud/platform/localcatalogue/tests.py @@ -725,11 +725,12 @@ def test_resource_uninstall(self): self.assertEqual(widgetT1.wait_loaded().error_count, 1) self.assertEqual(widgetT2.wait_loaded().error_count, 1) - # As the two Test v1.0 widgets - # one in the first tab and another in the second one - self.assertEqual(self.find_widget(title="Test 1").error_count, 1) - self.find_tab(title="Tab 2").click() - self.assertEqual(self.find_widget(title="Test 2").wait_loaded().error_count, 1) + with self.edit_mode as edit_session: + # As well as the two Test v1.0 widgets + # one in the first tab and another in the second one + self.assertEqual(self.find_widget(title="Test 1").error_count, 1) + self.find_tab(title="Tab 2").click() + self.assertEqual(self.find_widget(title="Test 2").wait_loaded().error_count, 1) @uses_extra_resources(( 'Wirecloud_Test_2.0.wgt', diff --git a/src/wirecloud/platform/static/js/wirecloud/UserInterfaceManager.js b/src/wirecloud/platform/static/js/wirecloud/UserInterfaceManager.js index d84d05a497..9cc9c2740c 100644 --- a/src/wirecloud/platform/static/js/wirecloud/UserInterfaceManager.js +++ b/src/wirecloud/platform/static/js/wirecloud/UserInterfaceManager.js @@ -229,7 +229,7 @@ options.effect = StyledElements.Alternatives.CROSS_DISSOLVE; } this.rootKeydownHandler = null; - this.alternatives.showAlternative(newView, options); + return this.alternatives.showAlternative(newView, options); }; UserInterfaceManager.handleEscapeEvent = function handleEscapeEvent()Ā { @@ -317,11 +317,10 @@ }; UserInterfaceManager.onHistoryChange = function onHistoryChange(state) { - this.changeCurrentView(state.view, { - onComplete: function (alternatives, oldView, nextView) { - if ('onHistoryChange' in nextView) { - nextView.onHistoryChange(state); - } + this.changeCurrentView(state.view, true).then((info) => { + let nextView = info.in; + if ('onHistoryChange' in nextView) { + nextView.onHistoryChange(state); } }); }; diff --git a/src/wirecloud/platform/static/js/wirecloud/Widget.js b/src/wirecloud/platform/static/js/wirecloud/Widget.js index 64acb59103..c49a24f0f9 100644 --- a/src/wirecloud/platform/static/js/wirecloud/Widget.js +++ b/src/wirecloud/platform/static/js/wirecloud/Widget.js @@ -395,6 +395,8 @@ * @returns {Wirecloud.Widget} */ reload: function reload() { + let priv = privates.get(this); + priv.status = STATUS.UNLOADING; this.wrapperElement.setAttribute('type', this.meta.codecontenttype); this.wrapperElement.contentWindow.location.reload(); @@ -593,7 +595,8 @@ var STATUS = { CREATED: 0, LOADING: 1, - RUNNING: 2 + RUNNING: 2, + UNLOADING: 3 }; var build_endpoints = function build_endpoints() { @@ -791,11 +794,15 @@ var on_unload = function on_unload() { - if (!this.loaded) { + let priv = privates.get(this); + + if (priv.status !== STATUS.RUNNING && priv.status !== STATUS.UNLOADING) { return; } - privates.get(this).status = STATUS.CREATED; + // Currently, the only scenario where current status can be "unloading" + // is when reloading the widget + priv.status = priv.status === STATUS.RUNNING ? STATUS.CREATED : STATUS.LOADING; this.prefCallback = null; remove_context_callbacks.call(this); diff --git a/src/wirecloud/platform/static/js/wirecloud/Widget/PreferencesWindowMenu.js b/src/wirecloud/platform/static/js/wirecloud/Widget/PreferencesWindowMenu.js index c2c588e2a6..48c70b3bd2 100644 --- a/src/wirecloud/platform/static/js/wirecloud/Widget/PreferencesWindowMenu.js +++ b/src/wirecloud/platform/static/js/wirecloud/Widget/PreferencesWindowMenu.js @@ -103,6 +103,8 @@ buttonArea: this.windowBottom }); this.form.insertInto(this.windowContent); + this.form.setdefaultsButton.addClassName('btn-set-defaults'); + this.form.cancelButton.addClassName('btn-cancel'); this.form.acceptButton.addClassName('btn-accept'); this.form.addEventListener('submit', this._savePrefs.bind(this)); this.form.addEventListener('cancel', this.hide.bind(this)); diff --git a/src/wirecloud/platform/static/js/wirecloud/ui/MarketplaceView.js b/src/wirecloud/platform/static/js/wirecloud/ui/MarketplaceView.js index 6726002fff..3a33c431d3 100644 --- a/src/wirecloud/platform/static/js/wirecloud/ui/MarketplaceView.js +++ b/src/wirecloud/platform/static/js/wirecloud/ui/MarketplaceView.js @@ -197,9 +197,11 @@ }; MarketplaceView.prototype.onHistoryChange = function onHistoryChange(state) { - this.changeCurrentMarket(state.market, {history: "ignore"}); - if ('onHistoryChange' in this.viewsByName[state.market]) { - this.viewsByName[state.market].onHistoryChange(state); + if (this.loading === false && state.market in this.viewsByName) { + this.changeCurrentMarket(state.market, {history: "ignore"}); + if ('onHistoryChange' in this.viewsByName[state.market]) { + this.viewsByName[state.market].onHistoryChange(state); + } } }; diff --git a/src/wirecloud/platform/static/js/wirecloud/ui/PreferencesWindowMenu.js b/src/wirecloud/platform/static/js/wirecloud/ui/PreferencesWindowMenu.js index 17db3e6a38..3126785c6f 100644 --- a/src/wirecloud/platform/static/js/wirecloud/ui/PreferencesWindowMenu.js +++ b/src/wirecloud/platform/static/js/wirecloud/ui/PreferencesWindowMenu.js @@ -191,6 +191,7 @@ // Reset button this.resetButton = new se.Button({ + class: 'btn-set-defaults', text: utils.gettext('Set Defaults'), }); this.resetButton.addEventListener("click", function () { diff --git a/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceView.js b/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceView.js index 298a4dbeb2..1f66be55be 100644 --- a/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceView.js +++ b/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceView.js @@ -44,6 +44,11 @@ }); this.editButton.addEventListener("click", (button) => { showHideTabBar.call(this, button.active); + if (!button.active) { + this.walletButton.active = false; + this.layout.slideOut(); + } + this.activeTab.dragboard._updateIWidgetSizes(true, true); }); this.walletButton = this.buildAddWidgetButton(); diff --git a/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceViewMenuItems.js b/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceViewMenuItems.js index 8a81a51eaf..fc9beb1d77 100644 --- a/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceViewMenuItems.js +++ b/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceViewMenuItems.js @@ -65,7 +65,7 @@ (new Wirecloud.ui.RenameWindowMenu(this, utils.gettext('Rename Workspace'))).show(); }.bind(this.workspace.model)); item.addIconClass("fa fa-pencil"); - item.setDisabled(!this.workspace.model.isAllowed('rename')); + item.setDisabled(!this.workspace.editing || !this.workspace.model.isAllowed('rename')); items.push(item); item = new se.MenuItem(utils.gettext("Share"), function () { @@ -95,7 +95,7 @@ this.showSettings(); }.bind(this.workspace)); item.addIconClass("fa fa-cog"); - item.setDisabled(!this.workspace.model.isAllowed('update_preferences')); + item.setDisabled(!this.workspace.editing || !this.workspace.model.isAllowed('update_preferences')); items.push(item); item = new se.MenuItem(utils.gettext("Remove"), () => { diff --git a/src/wirecloud/platform/tests/selenium.py b/src/wirecloud/platform/tests/selenium.py index ba35b3c5e2..46fea74cf7 100644 --- a/src/wirecloud/platform/tests/selenium.py +++ b/src/wirecloud/platform/tests/selenium.py @@ -58,27 +58,28 @@ def test_basic_workspace_operations(self): self.login(username="admin", next="/admin/Workspace") # admin only have one workspace, but WireCloud should allow any workspace operation - self.open_menu().check(('Rename', 'Settings', 'New workspace', 'Upload to my resources', 'Remove', 'Share', 'Embed')).close() + self.open_menu().check(('New workspace', 'Upload to my resources', 'Remove', 'Share', 'Embed')).close() self.create_workspace('Test') - # Now we have two workspaces, nothing should change - self.open_menu().check(('Rename', 'Settings', 'New workspace', 'Upload to my resources', 'Remove', 'Share', 'Embed'), ()).close() - self.rename_workspace('test2') - tab = self.find_tab(title="Tab") + # Now we have two workspaces, nothing should change except that now we are on edit mode + with self.edit_mode as edit_session: + self.open_menu().check(('Rename', 'Settings', 'New workspace', 'Upload to my resources', 'Remove', 'Share', 'Embed'), ()).close() + self.rename_workspace('test2') + tab = self.find_tab(title="Tab") - # Only one tab => we cannot remove it - tab.show_preferences().check(('Rename',), must_be_disabled=('Remove',)) + # Only one tab => we cannot remove it + tab.show_preferences().check(('Rename',), must_be_disabled=('Remove',)) - new_tab = self.create_tab() + new_tab = self.create_tab() - # Now we have two tabs so we can remove any of them - tab.show_preferences().check(must_be=('Rename', 'Remove')) - new_tab.click() - new_tab.show_preferences().check(must_be=('Rename', 'Remove')).close() + # Now we have two tabs so we can remove any of them + tab.show_preferences().check(must_be=('Rename', 'Remove')) + new_tab.click() + new_tab.show_preferences().check(must_be=('Rename', 'Remove')).close() - # Remove the recently created one (no confirmation needed as the tab is empty) - new_tab.remove() + # Remove the recently created one (no confirmation needed as the tab is empty) + new_tab.remove() self.remove_workspace() @@ -101,10 +102,11 @@ def test_move_iwidget_between_tabs(self): iwidget = self.find_tab(id="102").widgets[0] tab = self.find_tab(title='Tab 2') - ActionChains(self.driver).click_and_hold(iwidget.title_element).move_to_element(tab.element).release().perform() + with self.edit_mode as edit_session: + ActionChains(self.driver).click_and_hold(iwidget.title_element).move_to_element(tab.element).release().perform() - self.assertEqual(len(self.find_tab(id="102").widgets), src_iwidget_count - 1) - self.assertEqual(len(self.find_tab(id="103").widgets), dst_iwidget_count + 1) + self.assertEqual(len(self.find_tab(id="102").widgets), src_iwidget_count - 1) + self.assertEqual(len(self.find_tab(id="103").widgets), dst_iwidget_count + 1) test_move_iwidget_between_tabs.tags = tags + ('wirecloud-dragboard',) def test_create_widget_from_component_sidebar(self): @@ -113,34 +115,39 @@ def test_create_widget_from_component_sidebar(self): def test_remove_widget_from_workspace(self): self.login(username="user_with_workspaces", next="/user_with_workspaces/Workspace") - self.find_widget(title="Test 1").remove() + with self.edit_mode as edit_session: + self.find_widget(title="Test 1").remove() def test_remove_tab_from_workspace(self): self.login(username='user_with_workspaces', next='/user_with_workspaces/pending-events') self.find_tab(title="Tab 1").remove() - with self.wiring_view as wiring: - self.assertIsNone(wiring.find_draggable_component('widget', title="Test 1")) + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: + self.assertIsNone(wiring.find_draggable_component('widget', title="Test 1")) def test_tabs_with_read_only_widgets_cannot_be_removed(self): self.login(username='user_with_workspaces', next='/user_with_workspaces/pending-events') - tab = self.find_tab(title="Tab 2") - tab.show_preferences().check(must_be_disabled=('Remove',)) + with self.edit_mode as edit_session: + tab = self.find_tab(title="Tab 2") + tab.show_preferences().check(must_be_disabled=('Remove',)) - tab.click() - tab_widget = tab.find_widget(title="Test 2") - self.assertTrue(tab_widget.remove_button.is_disabled) + tab.click() + tab_widget = tab.find_widget(title="Test 2") + self.assertTrue(tab_widget.remove_button.is_disabled) def test_refresh_widget(self): self.login(username="user_with_workspaces", next="/user_with_workspaces/Workspace") - tab_widget = self.find_widget(title="Test 1") - with tab_widget: - last_received_event_field = self.driver.find_element_by_id('wiringOut') - self.driver.execute_script('arguments[0].textContent = "hello world!!";', last_received_event_field) + with self.edit_mode as edit_session: + tab_widget = self.find_widget(title="Test 1") + with tab_widget: + last_received_event_field = self.driver.find_element_by_id('wiringOut') + self.driver.execute_script('arguments[0].textContent = "hello world!!";', last_received_event_field) + + tab_widget.reload().wait_loaded() - tab_widget.reload() with tab_widget: last_received_event_field = self.wait_element_visible('#wiringOut') self.assertEqual(last_received_event_field.text, '') @@ -149,86 +156,89 @@ def test_refresh_widget(self): def test_basic_widget_functionalities(self): self.login(username="user_with_workspaces", next="/user_with_workspaces/Workspace") - iwidget = self.find_widget(title="Test 1") + with self.edit_mode as edit_session: + iwidget = self.find_widget(title="Test 1") - with iwidget: - check_default_settings_values(self) + with iwidget: + check_default_settings_values(self) - # Open widget settings - modal = iwidget.show_settings() + # Open widget settings + modal = iwidget.show_settings() - # Check dialog shows correct values - self.assertEqual(modal.get_field('list').value, "default") - self.assertEqual(modal.get_field('text').value, "initial text") - self.assertFalse(modal.get_field('boolean').is_selected) - self.assertEqual(modal.get_field('number').value, "2") - self.assertEqual(modal.get_field('password').value, "default") + # Check dialog shows correct values + self.assertEqual(modal.get_field('list').value, "default") + self.assertEqual(modal.get_field('text').value, "initial text") + self.assertFalse(modal.get_field('boolean').is_selected) + self.assertEqual(modal.get_field('number').value, "2") + self.assertEqual(modal.get_field('password').value, "default") - # Change widget settings - modal.get_field('list').set_value("1") # value1 - modal.get_field('text').set_value("test") - modal.get_field('boolean').click() - modal.get_field('number').set_value("0") - modal.get_field('password').set_value("password") + # Change widget settings + modal.get_field('list').set_value("1") # value1 + modal.get_field('text').set_value("test") + modal.get_field('boolean').click() + modal.get_field('number').set_value("0") + modal.get_field('password').set_value("password") - modal.accept() + modal.accept() - with iwidget: - self.assertEqual(self.driver.find_element_by_id('listPref').text, '1') - self.assertEqual(self.driver.find_element_by_id('textPref').text, 'test') - self.assertEqual(self.driver.find_element_by_id('booleanPref').text, 'true') - self.assertEqual(self.driver.find_element_by_id('numberPref').text, '0') - self.assertEqual(self.driver.find_element_by_id('passwordPref').text, 'password') + with iwidget: + self.assertEqual(self.driver.find_element_by_id('listPref').text, '1') + self.assertEqual(self.driver.find_element_by_id('textPref').text, 'test') + self.assertEqual(self.driver.find_element_by_id('booleanPref').text, 'true') + self.assertEqual(self.driver.find_element_by_id('numberPref').text, '0') + self.assertEqual(self.driver.find_element_by_id('passwordPref').text, 'password') - # Open widget settings again - modal = iwidget.show_settings() + # Open widget settings again + modal = iwidget.show_settings() - # Check dialog shows correct values - self._check_modified_widget_preferences(modal) + # Check dialog shows correct values + self._check_modified_widget_preferences(modal) - modal.accept() + modal.accept() self.reload() + self.wait_wirecloud_ready(login=True) WebDriverWait(self.driver, timeout=15).until(lambda driver: self.active_tab is not None) - iwidget = self.find_widget(title="Test 1") + with self.edit_mode as edit_session: + iwidget = self.find_widget(title="Test 1") - # Open widget settings again - modal = iwidget.show_settings() + # Open widget settings again + modal = iwidget.show_settings() - # Check dialog shows correct values - self._check_modified_widget_preferences(modal) + # Check dialog shows correct values + self._check_modified_widget_preferences(modal) - # Change widget settings - modal.get_field('text').set_value("") - modal.get_field('password').set_value("") + # Change widget settings + modal.get_field('text').set_value("") + modal.get_field('password').set_value("") - modal.accept() + modal.accept() - with iwidget: - self.assertEqual(self.driver.find_element_by_id('listPref').text, '1') - self.assertEqual(self.driver.find_element_by_id('textPref').text, '') - self.assertEqual(self.driver.find_element_by_id('booleanPref').text, 'true') - self.assertEqual(self.driver.find_element_by_id('numberPref').text, '0') - self.assertEqual(self.driver.find_element_by_id('passwordPref').text, '') + with iwidget: + self.assertEqual(self.driver.find_element_by_id('listPref').text, '1') + self.assertEqual(self.driver.find_element_by_id('textPref').text, '') + self.assertEqual(self.driver.find_element_by_id('booleanPref').text, 'true') + self.assertEqual(self.driver.find_element_by_id('numberPref').text, '0') + self.assertEqual(self.driver.find_element_by_id('passwordPref').text, '') - # Restore default widget settings - modal = iwidget.show_settings() - modal.find_button("Set Defaults").click() - modal.accept() + # Restore default widget settings + modal = iwidget.show_settings() + modal.find_button("Set Defaults").click() + modal.accept() - with iwidget: - check_default_settings_values(self) + with iwidget: + check_default_settings_values(self) - # Use api test widget to test other API features - self.network._servers['http']['example.com'].add_response('GET', '/success.html', {'content': 'remote makerequest was successful'}) - api_test_iwidget = self.create_widget("Wirecloud API test") - api_test_iwidget_id = api_test_iwidget.id + # Use api test widget to test other API features + self.network._servers['http']['example.com'].add_response('GET', '/success.html', {'content': 'remote makerequest was successful'}) + api_test_iwidget = self.create_widget("Wirecloud API test") + api_test_iwidget_id = api_test_iwidget.id - # Open widget settings again - modal = api_test_iwidget.show_settings() - modal.get_field('text').set_value("Success!!") - modal.accept() + # Open widget settings again + modal = api_test_iwidget.show_settings() + modal.get_field('text').set_value("Success!!") + modal.accept() expected_value = 'new value' @@ -295,22 +305,23 @@ def test_basic_widget_functionalities(self): def test_resize_widgets(self): self.login(username="user_with_workspaces", next="/user_with_workspaces/Workspace") - widget1 = self.widgets[1] - old_size = widget1.size - old_position = widget1.position + with self.edit_mode as edit_session: + widget1 = self.widgets[1] + old_size = widget1.size + old_position = widget1.position - widget1.resize('bottom_left', -30, 30) - new_size = widget1.size - new_position = widget1.position - self.assertNotEqual(new_size, old_size) - self.assertNotEqual(new_position, old_position) + widget1.resize('bottom_left', -30, 30) + new_size = widget1.size + new_position = widget1.position + self.assertNotEqual(new_size, old_size) + self.assertNotEqual(new_position, old_position) # Django uses http 1.0 by default # If-Modified-Since has a 1 second resolution time.sleep(1) self.reload() - self.wait_wirecloud_ready() + self.wait_wirecloud_ready(login=True) widget1 = self.widgets[1] self.assertEqual(new_size, widget1.size) @@ -387,21 +398,23 @@ def test_http_cache(self): self.assertEqual(self.get_current_workspace_title(), 'Test') # Add a new tab - self.create_tab() + with self.edit_mode as edit_session: + self.create_tab() self.reload() - self.wait_wirecloud_ready() + self.wait_wirecloud_ready(login=True) tabs = len(self.tabs) self.assertEqual(tabs, 2) - tab = self.find_tab(title='Tab') + with self.edit_mode as edit_session: + tab = self.find_tab(title='Tab') - # Rename the created tab - tab.rename('Other Name') + # Rename the created tab + tab.rename('Other Name') self.reload() - self.wait_wirecloud_ready() + self.wait_wirecloud_ready(login=True) self.assertEqual(len(self.tabs), 2) tab = self.find_tab(title='Other Name') @@ -410,52 +423,56 @@ def test_http_cache(self): self.assertIsNone(tab) # Add two widgets to the mashup - with self.resource_sidebar as sidebar: - resource = sidebar.search_component('widget', 'Test') - resource.create_component() - resource.create_component() + with self.edit_mode as edit_session: + with edit_session.resource_sidebar as sidebar: + resource = sidebar.search_component('widget', 'Test') + resource.create_component() + resource.create_component() self.reload() - self.wait_wirecloud_ready() + self.wait_wirecloud_ready(login=True) self.assertEqual(len(self.widgets), 2) # Rename a widget - - iwidget = self.widgets[1] - iwidget.rename('Other Test') + with self.edit_mode as edit_session: + iwidget = self.widgets[1] + iwidget.rename('Other Test') self.reload() - self.wait_wirecloud_ready() + self.wait_wirecloud_ready(login=True) - iwidget = self.widgets[0] - self.assertEqual(iwidget.title, 'Test') + with self.edit_mode as edit_session: + iwidget = self.widgets[0] + self.assertEqual(iwidget.title, 'Test') - iwidget = self.widgets[1] - self.assertEqual(iwidget.title, 'Other Test') + iwidget = self.widgets[1] + self.assertEqual(iwidget.title, 'Other Test') - # Remove a widget - iwidget.remove() + # Remove a widget + iwidget.remove() self.reload() - self.wait_wirecloud_ready() + self.wait_wirecloud_ready(login=True) self.assertEqual(len(self.widgets), 1) # Rename the workspace - self.rename_workspace('test2') + with self.edit_mode as edit_session: + self.rename_workspace('test2') self.reload() - self.wait_wirecloud_ready() + self.wait_wirecloud_ready(login=True) self.assertEqual(self.get_current_workspace_title(), 'test2') # Remove the tab with widgets - tab = self.find_tab(title='Other Name') - tab.remove() + with self.edit_mode as edit_session: + tab = self.find_tab(title='Other Name') + tab.remove() self.reload() - self.wait_wirecloud_ready() + self.wait_wirecloud_ready(login=True) self.assertEqual(len(self.tabs), 1) self.assertEqual(len(self.widgets), 0) @@ -502,33 +519,32 @@ def test_create_workspace_from_catalogue_using_parameters(self): iwidget = self.widgets[0] - iwidget.open_menu().click_entry('Settings') - - self.assertEqual(self.driver.find_element_by_css_selector('.window_menu [name="list"]').get_attribute('value'), 'default') - text_pref = self.driver.find_element_by_css_selector('.window_menu [name="text"]') - self.assertEqual(text_pref.get_attribute('disabled'), 'true') - self.assertEqual(text_pref.get_attribute('value'), 'parameterized value') - - self.assertFalse(self.driver.find_element_by_css_selector('.window_menu [name="boolean"]').is_selected()) - password_prefs = self.driver.find_elements_by_css_selector('.window_menu [name="password"]') - self.assertEqual(len(password_prefs), 0) - - self.driver.find_element_by_xpath("//*[contains(@class, 'window_menu')]//*[text()='Cancel']").click() - - with iwidget: - self.assertEqual(self.driver.find_element_by_id('listPref').text, 'default') - self.assertEqual(self.driver.find_element_by_id('textPref').text, 'parameterized value') - self.assertEqual(self.driver.find_element_by_id('booleanPref').text, 'false') - self.assertEqual(self.driver.find_element_by_id('passwordPref').text, 'parameterized password') - - with self.wiring_view as wiring: - operator = wiring.find_draggable_component('operator', title="TestOperator") - - modal = operator.show_settings() - prefix_field = modal.get_field('prefix') - self.assertEqual(prefix_field.get_attribute('disabled'), 'true') - self.assertEqual(prefix_field.get_attribute('value'), 'parameterized value: ') - modal.accept() + with self.edit_mode as edit_session: + iwidget.open_menu().click_entry('Settings') + + form = FormModalTester(self, self.wait_element_visible(".wc-component-preferences-modal")) + self.assertEqual(form.get_field('list').value, 'default') + text_pref = form.get_field('text') + self.assertTrue(text_pref.is_disabled) + self.assertEqual(text_pref.value, 'parameterized value') + self.assertFalse(form.get_field('boolean').is_selected) + self.assertRaises(NoSuchElementException, form.get_field, 'password') + form.cancel() + + with iwidget: + self.assertEqual(self.driver.find_element_by_id('listPref').text, 'default') + self.assertEqual(self.driver.find_element_by_id('textPref').text, 'parameterized value') + self.assertEqual(self.driver.find_element_by_id('booleanPref').text, 'false') + self.assertEqual(self.driver.find_element_by_id('passwordPref').text, 'parameterized password') + + with edit_session.wiring_view as wiring: + operator = wiring.find_draggable_component('operator', title="TestOperator") + + modal = operator.show_settings() + prefix_field = modal.get_field('prefix') + self.assertEqual(prefix_field.get_attribute('disabled'), 'true') + self.assertEqual(prefix_field.get_attribute('value'), 'parameterized value: ') + modal.accept() def test_create_workspace_from_catalogue_duplicated_workspaces(self): @@ -566,9 +582,10 @@ def test_merge_mashup(self): self.login(username="admin", next="/admin/Workspace") - with self.resource_sidebar as sidebar: - resource = sidebar.search_component('mashup', 'Test Mashup') - resource.merge() + with self.edit_mode as edit_session: + with edit_session.resource_sidebar as sidebar: + resource = sidebar.search_component('mashup', 'Test Mashup') + resource.merge() self.assertEqual(len(self.tabs), 3) tab1 = self.find_tab(name='tab') @@ -614,14 +631,15 @@ def test_workspace_publish_readonly_widgets_and_connections(self): 'readOnlyConnectables': True, }) self.create_workspace(mashup='Published Workspace') - iwidget = self.widgets[0] - close_button = ButtonTester(self, iwidget.element.find_element_by_css_selector('.fa-remove')) - self.assertTrue(close_button.is_disabled) - # bypass the internal call to element_be_clickable as the button is usually hidden - close_button.element.click() + with self.edit_mode as edit_session: + iwidget = self.widgets[0] + close_button = ButtonTester(self, iwidget.element.find_element_by_css_selector('.fa-remove')) + self.assertTrue(close_button.is_disabled) + # bypass the internal call to element_be_clickable as the button is usually hidden + close_button.element.click() - with self.wiring_view as wiring: - self.assertEqual(len(wiring.find_connections(extra_class="readonly")), 3) + with edit_session.wiring_view as wiring: + self.assertEqual(len(wiring.find_connections(extra_class="readonly")), 3) self.assertEqual(len(self.widgets), 2) @@ -661,7 +679,7 @@ def test_public_workspaces_anonymous_user(self): url = self.live_server_url + '/user_with_workspaces/public-workspace' self.driver.get(url) - self.wait_wirecloud_ready() + self.wait_wirecloud_ready(login=True) self.assertIsNone(self.find_navbar_button("wc-show-component-sidebar-button")) self.assertIsNone(self.find_navbar_button("wc-show-wiring-button")) @@ -678,7 +696,7 @@ def test_public_workspaces_anonymous_user(self): form.get_field('password').set_value('admin') form.submit() - self.wait_wirecloud_ready() + self.wait_wirecloud_ready(login=True) self.assertEqual(self.get_current_workspace_title(), 'Public Workspace') def test_embedded_view(self): @@ -705,12 +723,11 @@ def check_public_workspace(self, frame_id=None): target_iwidget = iwidgets[0] source_iwidget.wait_loaded() target_iwidget.wait_loaded() - source_iwidget.open_menu().check(must_be_disabled=('Rename', 'Settings', 'Upgrade/Downgrade', 'Full Dragboard', 'Extract from grid')).close() - target_iwidget.open_menu().check(must_be_disabled=('Rename', 'Settings', 'Upgrade/Downgrade', 'Full Dragboard', 'Extract from grid')) + self.assertFalse(source_iwidget.btn_preferences.is_displayed) + self.assertFalse(target_iwidget.btn_preferences.is_displayed) - tab = self.find_tab(title='Tab') + tab = self.tabs[0] self.assertRaises(NoSuchElementException, tab.element.find_element_by_css_selector, '.icon-tab-menu') - self.assertRaises(NoSuchElementException, self.driver.find_element_by_css_selector, '.icon-add-tab') # Check wiring works @@ -727,8 +744,9 @@ def test_browser_navigation_history_management(self): self.login(username='user_with_workspaces', next="/user_with_workspaces/Workspace") - with self.wiring_view: - pass + with self.edit_mode as edit_session: + with edit_session.wiring_view: + pass with self.marketplace_view: pass @@ -744,7 +762,7 @@ def test_browser_navigation_history_management(self): WebDriverWait(self.driver, timeout=10).until(lambda driver: self.driver.current_url == self.live_server_url + '/login?next=/user_with_workspaces/Workspace') self.driver.forward() - self.wait_wirecloud_ready() + self.wait_wirecloud_ready(login=True) self.assertEqual(self.get_current_workspace_title(), 'Workspace') self.driver.forward() @@ -798,7 +816,7 @@ def test_browser_workspace_navigation(self): # Replay navigation history self.driver.forward() - self.wait_wirecloud_ready() + self.wait_wirecloud_ready(login=True) WebDriverWait(self.driver, timeout=10).until( WEC.workspace(self, owner='user_with_workspaces', name='Pending Events', tab='Tab 1') ) @@ -835,7 +853,8 @@ def test_browser_navigation_from_renamed_tab(self): initial_workspace_tab = self.active_tab initial_workspace_tab_name = initial_workspace_tab.title - self.find_tab(title='Tab 2').click().rename('NewName') + with self.edit_mode as edit_session: + self.find_tab(title='Tab 2').click().rename('NewName') initial_workspace_tab.click() WebDriverWait(self.driver, 5).until(WEC.workspace(self, tab=initial_workspace_tab_name)) @@ -858,7 +877,8 @@ def test_browser_navigation_from_renamed_workspace(self): self.login(username="user_with_workspaces", next="/user_with_workspaces/Workspace") self.change_current_workspace('Pending Events') - self.rename_workspace('New Name') + with self.edit_mode as edit_session: + self.rename_workspace('New Name') self.change_current_workspace('ExistingWorkspace') @@ -894,8 +914,9 @@ def test_browser_navigation_to_deleted_workspace(self): WebDriverWait(self.driver, 10).until(WEC.workspace(self, owner='user_with_workspaces', name='Workspace')) # "Workspace" workspace should be editable - self.assertFalse(self.find_navbar_button("wc-show-component-sidebar-button").is_disabled) - self.assertFalse(self.find_navbar_button("wc-show-wiring-button").is_disabled) + self.assertFalse(self.find_navbar_button("wc-edit-mode-button").is_disabled) + self.assertTrue(self.find_navbar_button("wc-show-component-sidebar-button").is_disabled) + self.assertTrue(self.find_navbar_button("wc-show-wiring-button").is_disabled) def assertElementHasFocus(self, element): # Workaround webkit problem with xhtml and retreiving element with focus @@ -926,18 +947,19 @@ def test_gui_tutorials(self): next_button.click() WebDriverWait(self.driver, 10).until(WEC.element_be_clickable((By.CSS_SELECTOR, ".wc-toolbar .wc-show-component-sidebar-button"))) - with self.resource_sidebar as sidebar: + with self.edit_mode as edit_session: + with edit_session.resource_sidebar as sidebar: - # Add the youtube browser widget - WebDriverWait(self.driver, timeout=15).until(WEC.component_instantiable(sidebar, 'YouTube Browser')) + # Add the youtube browser widget + WebDriverWait(self.driver, timeout=15).until(WEC.component_instantiable(sidebar, 'YouTube Browser')) - # Next tutorial step - next_button = self.wait_element_visible_by_xpath("//*[contains(@class, 'window_menu')]//*[text()='Next']") - self.assertElementHasFocus(next_button) - next_button.click() + # Next tutorial step + next_button = self.wait_element_visible_by_xpath("//*[contains(@class, 'window_menu')]//*[text()='Next']") + self.assertElementHasFocus(next_button) + next_button.click() - # Add the input box widget - WebDriverWait(self.driver, timeout=15).until(WEC.component_instantiable(sidebar, 'Input Box')) + # Add the input box widget + WebDriverWait(self.driver, timeout=15).until(WEC.component_instantiable(sidebar, 'Input Box')) # cancel current tutorial self.wait_element_visible_by_xpath("//*[contains(@class, 'window_menu')]//*[text()='Cancel']").click() @@ -951,15 +973,16 @@ def test_move_widget_and_restore(self): iwidgets = self.widgets - self.assertEqual(iwidgets[0].layout_position, (0, 0)) - self.assertEqual(iwidgets[1].layout_position, (6, 0)) + with self.edit_mode as edit_session: + self.assertEqual(iwidgets[0].layout_position, (0, 0)) + self.assertEqual(iwidgets[1].layout_position, (6, 0)) - offset = iwidgets[1].element.location['x'] - iwidgets[0].element.location['x'] - ActionChains(self.driver).click_and_hold(iwidgets[0].title_element).move_by_offset(offset, 0).release().perform() - WebDriverWait(self.driver, timeout=5).until(lambda driver: iwidgets[0].layout_position == (6, 0) and iwidgets[1].layout_position == (6, 24)) + offset = iwidgets[1].element.location['x'] - iwidgets[0].element.location['x'] + ActionChains(self.driver).click_and_hold(iwidgets[0].title_element).move_by_offset(offset, 0).release().perform() + WebDriverWait(self.driver, timeout=5).until(lambda driver: iwidgets[0].layout_position == (6, 0) and iwidgets[1].layout_position == (6, 24)) - ActionChains(self.driver).click_and_hold(iwidgets[0].title_element).move_by_offset(-offset, 300).release().perform() - WebDriverWait(self.driver, timeout=5).until(lambda driver: iwidgets[0].layout_position == (0, 0) and iwidgets[1].layout_position == (6, 0)) + ActionChains(self.driver).click_and_hold(iwidgets[0].title_element).move_by_offset(-offset, 300).release().perform() + WebDriverWait(self.driver, timeout=5).until(lambda driver: iwidgets[0].layout_position == (0, 0) and iwidgets[1].layout_position == (6, 0)) test_move_widget_and_restore.tags = tags + ('wirecloud-dragboard',) @@ -972,18 +995,19 @@ def test_move_widget_and_restore_touch(self): iwidgets = self.widgets - self.assertEqual(iwidgets[0].layout_position, (0, 0)) - self.assertEqual(iwidgets[1].layout_position, (6, 0)) + with self.edit_mode as edit_session: + self.assertEqual(iwidgets[0].layout_position, (0, 0)) + self.assertEqual(iwidgets[1].layout_position, (6, 0)) - iwidgets[0].wait_loaded() + iwidgets[0].wait_loaded() - title_location = iwidgets[0].title_element.location - TouchActions(self.driver).tap_and_hold(title_location['x'] + 10, title_location['y'] + 10).move(330, title_location['y'] + 10).release(990, 300).perform() - WebDriverWait(self.driver, timeout=5).until(lambda driver: iwidgets[0].layout_position == (6, 0) and iwidgets[1].layout_position == (6, 24)) + title_location = iwidgets[0].title_element.location + TouchActions(self.driver).tap_and_hold(title_location['x'] + 10, title_location['y'] + 10).move(330, title_location['y'] + 10).release(990, 300).perform() + WebDriverWait(self.driver, timeout=5).until(lambda driver: iwidgets[0].layout_position == (6, 0) and iwidgets[1].layout_position == (6, 24)) - title_location = iwidgets[0].title_element.location - TouchActions(self.driver).tap_and_hold(title_location['x'] + 10, title_location['y'] + 10).move(0, 300).release(0, 300).perform() - WebDriverWait(self.driver, timeout=5).until(lambda driver: iwidgets[0].layout_position == (0, 0) and iwidgets[1].layout_position == (6, 0)) + title_location = iwidgets[0].title_element.location + TouchActions(self.driver).tap_and_hold(title_location['x'] + 10, title_location['y'] + 10).move(0, 300).release(0, 300).perform() + WebDriverWait(self.driver, timeout=5).until(lambda driver: iwidgets[0].layout_position == (0, 0) and iwidgets[1].layout_position == (6, 0)) test_move_widget_and_restore_touch.tags = tags + ('wirecloud-dragboard',) @@ -992,105 +1016,106 @@ def test_basic_add_and_move_widget(self): self.login(username="admin", next="/admin/Workspace") - with self.resource_sidebar as sidebar: - resource = sidebar.search_component('widget', 'Context Inspector') - widget1 = resource.create_component() - widget2 = resource.create_component() - - initial_widget1_position = widget1.layout_position - with widget1: - position_from_context = ( - int(self.driver.find_element_by_css_selector('[data-name="xPosition"] .content').text), - int(self.driver.find_element_by_css_selector('[data-name="yPosition"] .content').text), - ) - self.assertEqual(position_from_context, initial_widget1_position) - initial_widget1_xPosition_changes = self.driver.find_element_by_css_selector('[data-name="xPosition"] .badge').text - initial_widget1_yPosition_changes = self.driver.find_element_by_css_selector('[data-name="yPosition"] .badge').text - - with widget2: - initial_widget2_xPosition_changes = self.driver.find_element_by_css_selector('[data-name="xPosition"] .badge').text - self.assertEqual(initial_widget2_xPosition_changes, '0') - - initial_widget2_yPosition_changes = self.driver.find_element_by_css_selector('[data-name="yPosition"] .badge').text - self.assertEqual(initial_widget2_yPosition_changes, '0') - - # Move widget2 moving widget1 as side effect - self.driver.execute_script(''' - var view = Wirecloud.UserInterfaceManager.views.workspace; - var layout = view.activeTab.dragboard.baseLayout; - var widget = view.activeTab.findWidget(%s); - layout.initializeMove(widget); - layout.moveTemporally(3, 0); - layout.acceptMove(); - ''' % widget2.id) - - self.assertEqual(widget1.layout_position, (initial_widget1_position[0], 24)) - self.assertEqual(widget2.layout_position, (3, 0)) - - with widget1: - xPosition_changes = self.driver.find_element_by_css_selector('[data-name="xPosition"] .badge').text - self.assertEqual(xPosition_changes, initial_widget1_xPosition_changes) - - yPosition_changes = self.driver.find_element_by_css_selector('[data-name="yPosition"] .badge').text - self.assertEqual(yPosition_changes, str(int(initial_widget1_yPosition_changes) + 1)) - - height_changes = self.driver.find_element_by_css_selector('[data-name="heightInPixels"] .badge').text - self.assertEqual(height_changes, "0") - - width_changes = self.driver.find_element_by_css_selector('[data-name="widthInPixels"] .badge').text - self.assertEqual(width_changes, "0") - - with widget2: - xPosition_changes = self.driver.find_element_by_css_selector('[data-name="xPosition"] .badge').text - self.assertEqual(xPosition_changes, str(int(initial_widget2_xPosition_changes) + 1)) - - yPosition_changes = self.driver.find_element_by_css_selector('[data-name="yPosition"] .badge').text - self.assertEqual(yPosition_changes, initial_widget2_yPosition_changes) - - height_changes = self.driver.find_element_by_css_selector('[data-name="heightInPixels"] .badge').text - self.assertEqual(height_changes, "0") - - width_changes = self.driver.find_element_by_css_selector('[data-name="widthInPixels"] .badge').text - self.assertEqual(width_changes, "0") - - # Move widget2 again without affecting widget1 - self.driver.execute_script(''' - var view = Wirecloud.UserInterfaceManager.views.workspace; - var layout = view.activeTab.dragboard.baseLayout; - var widget = view.activeTab.findWidget(%s); - layout.initializeMove(widget); - layout.moveTemporally(0, 3); - layout.acceptMove(); - ''' % widget2.id) - - self.assertEqual(widget1.layout_position, (initial_widget1_position[0], 24)) - self.assertEqual(widget2.layout_position, (0, 0)) - - with widget1: - xPosition_changes = self.driver.find_element_by_css_selector('[data-name="xPosition"] .badge').text - self.assertEqual(xPosition_changes, initial_widget1_xPosition_changes) - - yPosition_changes = self.driver.find_element_by_css_selector('[data-name="yPosition"] .badge').text - self.assertEqual(yPosition_changes, str(int(initial_widget1_yPosition_changes) + 1)) - - height_changes = self.driver.find_element_by_css_selector('[data-name="heightInPixels"] .badge').text - self.assertEqual(height_changes, "0") - - width_changes = self.driver.find_element_by_css_selector('[data-name="widthInPixels"] .badge').text - self.assertEqual(width_changes, "0") - - with widget2: - xPosition_changes = self.driver.find_element_by_css_selector('[data-name="xPosition"] .badge').text - self.assertEqual(xPosition_changes, str(int(initial_widget2_xPosition_changes) + 2)) - - yPosition_changes = self.driver.find_element_by_css_selector('[data-name="yPosition"] .badge').text - self.assertEqual(yPosition_changes, initial_widget2_yPosition_changes) - - height_changes = self.driver.find_element_by_css_selector('[data-name="heightInPixels"] .badge').text - self.assertEqual(height_changes, "0") - - width_changes = self.driver.find_element_by_css_selector('[data-name="widthInPixels"] .badge').text - self.assertEqual(width_changes, "0") + with self.edit_mode as edit_session: + with edit_session.resource_sidebar as sidebar: + resource = sidebar.search_component('widget', 'Context Inspector') + widget1 = resource.create_component() + widget2 = resource.create_component() + + initial_widget1_position = widget1.layout_position + with widget1: + position_from_context = ( + int(self.driver.find_element_by_css_selector('[data-name="xPosition"] .content').text), + int(self.driver.find_element_by_css_selector('[data-name="yPosition"] .content').text), + ) + self.assertEqual(position_from_context, initial_widget1_position) + initial_widget1_xPosition_changes = self.driver.find_element_by_css_selector('[data-name="xPosition"] .badge').text + initial_widget1_yPosition_changes = self.driver.find_element_by_css_selector('[data-name="yPosition"] .badge').text + + with widget2: + initial_widget2_xPosition_changes = self.driver.find_element_by_css_selector('[data-name="xPosition"] .badge').text + self.assertEqual(initial_widget2_xPosition_changes, '0') + + initial_widget2_yPosition_changes = self.driver.find_element_by_css_selector('[data-name="yPosition"] .badge').text + self.assertEqual(initial_widget2_yPosition_changes, '0') + + # Move widget2 moving widget1 as side effect + self.driver.execute_script(''' + var view = Wirecloud.UserInterfaceManager.views.workspace; + var layout = view.activeTab.dragboard.baseLayout; + var widget = view.activeTab.findWidget(%s); + layout.initializeMove(widget); + layout.moveTemporally(3, 0); + layout.acceptMove(); + ''' % widget2.id) + + self.assertEqual(widget1.layout_position, (initial_widget1_position[0], 24)) + self.assertEqual(widget2.layout_position, (3, 0)) + + with widget1: + xPosition_changes = self.driver.find_element_by_css_selector('[data-name="xPosition"] .badge').text + self.assertEqual(xPosition_changes, initial_widget1_xPosition_changes) + + yPosition_changes = self.driver.find_element_by_css_selector('[data-name="yPosition"] .badge').text + self.assertEqual(yPosition_changes, str(int(initial_widget1_yPosition_changes) + 1)) + + height_changes = self.driver.find_element_by_css_selector('[data-name="heightInPixels"] .badge').text + self.assertEqual(height_changes, "0") + + width_changes = self.driver.find_element_by_css_selector('[data-name="widthInPixels"] .badge').text + self.assertEqual(width_changes, "0") + + with widget2: + xPosition_changes = self.driver.find_element_by_css_selector('[data-name="xPosition"] .badge').text + self.assertEqual(xPosition_changes, str(int(initial_widget2_xPosition_changes) + 1)) + + yPosition_changes = self.driver.find_element_by_css_selector('[data-name="yPosition"] .badge').text + self.assertEqual(yPosition_changes, initial_widget2_yPosition_changes) + + height_changes = self.driver.find_element_by_css_selector('[data-name="heightInPixels"] .badge').text + self.assertEqual(height_changes, "0") + + width_changes = self.driver.find_element_by_css_selector('[data-name="widthInPixels"] .badge').text + self.assertEqual(width_changes, "0") + + # Move widget2 again without affecting widget1 + self.driver.execute_script(''' + var view = Wirecloud.UserInterfaceManager.views.workspace; + var layout = view.activeTab.dragboard.baseLayout; + var widget = view.activeTab.findWidget(%s); + layout.initializeMove(widget); + layout.moveTemporally(0, 3); + layout.acceptMove(); + ''' % widget2.id) + + self.assertEqual(widget1.layout_position, (initial_widget1_position[0], 24)) + self.assertEqual(widget2.layout_position, (0, 0)) + + with widget1: + xPosition_changes = self.driver.find_element_by_css_selector('[data-name="xPosition"] .badge').text + self.assertEqual(xPosition_changes, initial_widget1_xPosition_changes) + + yPosition_changes = self.driver.find_element_by_css_selector('[data-name="yPosition"] .badge').text + self.assertEqual(yPosition_changes, str(int(initial_widget1_yPosition_changes) + 1)) + + height_changes = self.driver.find_element_by_css_selector('[data-name="heightInPixels"] .badge').text + self.assertEqual(height_changes, "0") + + width_changes = self.driver.find_element_by_css_selector('[data-name="widthInPixels"] .badge').text + self.assertEqual(width_changes, "0") + + with widget2: + xPosition_changes = self.driver.find_element_by_css_selector('[data-name="xPosition"] .badge').text + self.assertEqual(xPosition_changes, str(int(initial_widget2_xPosition_changes) + 2)) + + yPosition_changes = self.driver.find_element_by_css_selector('[data-name="yPosition"] .badge').text + self.assertEqual(yPosition_changes, initial_widget2_yPosition_changes) + + height_changes = self.driver.find_element_by_css_selector('[data-name="heightInPixels"] .badge').text + self.assertEqual(height_changes, "0") + + width_changes = self.driver.find_element_by_css_selector('[data-name="widthInPixels"] .badge').text + self.assertEqual(width_changes, "0") test_basic_add_and_move_widget.tags = tags + ('wirecloud-dragboard',) def test_move_widget_interchange(self): @@ -1099,32 +1124,33 @@ def test_move_widget_interchange(self): iwidgets = self.widgets - self.assertEqual(iwidgets[0].layout_position, (0, 0)) - self.assertEqual(iwidgets[1].layout_position, (6, 0)) - - self.driver.execute_script(''' - var view = Wirecloud.UserInterfaceManager.views.workspace; - var layout = view.activeTab.dragboard.baseLayout; - var widget = view.activeTab.findWidget(%s); - layout.initializeMove(widget); - layout.moveTemporally(6, 25); - layout.acceptMove(); - ''' % iwidgets[0].id) - - self.assertEqual(iwidgets[0].layout_position, (6, 24)) - self.assertEqual(iwidgets[1].layout_position, (6, 0)) - - self.driver.execute_script(''' - var view = Wirecloud.UserInterfaceManager.views.workspace; - var layout = view.activeTab.dragboard.baseLayout; - var widget = view.activeTab.findWidget(%s); - layout.initializeMove(widget); - layout.moveTemporally(0, 0); - layout.acceptMove(); - ''' % iwidgets[1].id) - - self.assertEqual(iwidgets[0].layout_position, (6, 0)) - self.assertEqual(iwidgets[1].layout_position, (0, 0)) + with self.edit_mode as edit_session: + self.assertEqual(iwidgets[0].layout_position, (0, 0)) + self.assertEqual(iwidgets[1].layout_position, (6, 0)) + + self.driver.execute_script(''' + var view = Wirecloud.UserInterfaceManager.views.workspace; + var layout = view.activeTab.dragboard.baseLayout; + var widget = view.activeTab.findWidget(%s); + layout.initializeMove(widget); + layout.moveTemporally(6, 25); + layout.acceptMove(); + ''' % iwidgets[0].id) + + self.assertEqual(iwidgets[0].layout_position, (6, 24)) + self.assertEqual(iwidgets[1].layout_position, (6, 0)) + + self.driver.execute_script(''' + var view = Wirecloud.UserInterfaceManager.views.workspace; + var layout = view.activeTab.dragboard.baseLayout; + var widget = view.activeTab.findWidget(%s); + layout.initializeMove(widget); + layout.moveTemporally(0, 0); + layout.acceptMove(); + ''' % iwidgets[1].id) + + self.assertEqual(iwidgets[0].layout_position, (6, 0)) + self.assertEqual(iwidgets[1].layout_position, (0, 0)) test_move_widget_interchange.tags = tags + ('wirecloud-dragboard',) @@ -1134,12 +1160,14 @@ def test_extract_widget_from_grid(self): self.login(username="admin", next="/admin/GridLayoutTests") iwidget = self.widgets[0] - _, old_size = self.get_widget_sizes_from_context(iwidget) - iwidget.open_menu().click_entry('Extract from grid') - _, new_size = self.get_widget_sizes_from_context(iwidget.wait_still()) + with self.edit_mode as edit_session: + _, old_size = self.get_widget_sizes_from_context(iwidget) - self.assertEqual(old_size, new_size) + iwidget.open_menu().click_entry('Extract from grid') + _, new_size = self.get_widget_sizes_from_context(iwidget.wait_still()) + + self.assertEqual(old_size, new_size) test_extract_widget_from_grid.tags = tags + ('wirecloud-dragboard',) @uses_extra_resources(('Wirecloud_context-inspector_0.5.wgt',), shared=True) @@ -1148,24 +1176,25 @@ def test_minimize_widget(self): self.login(username="user_with_workspaces", next="/user_with_workspaces/ColumnLayoutTests") - iwidget = self.widgets[0] - affected_iwidget = self.widgets[2] - old_size, old_size_in_pixels = self.get_widget_sizes_from_context(iwidget) - old_affected_iwidget_position = affected_iwidget.layout_position + with self.edit_mode as edit_session: + iwidget = self.widgets[0] + affected_iwidget = self.widgets[2] + old_size, old_size_in_pixels = self.get_widget_sizes_from_context(iwidget) + old_affected_iwidget_position = affected_iwidget.layout_position - iwidget.minimize() - minimized_size, minimized_size_in_pixels = self.get_widget_sizes_from_context(iwidget) - self.assertEqual(minimized_size[0], old_size[0]) - self.assertLess(minimized_size[1], old_size[1]) - self.assertEqual(minimized_size_in_pixels, (old_size_in_pixels[0], 0)) - self.assertEqual(affected_iwidget.layout_position, (0, minimized_size[1])) + iwidget.minimize() + minimized_size, minimized_size_in_pixels = self.get_widget_sizes_from_context(iwidget) + self.assertEqual(minimized_size[0], old_size[0]) + self.assertLess(minimized_size[1], old_size[1]) + self.assertEqual(minimized_size_in_pixels, (old_size_in_pixels[0], 0)) + self.assertEqual(affected_iwidget.layout_position, (0, minimized_size[1])) - iwidget.maximize() - new_size, new_size_in_pixels = self.get_widget_sizes_from_context(iwidget) + iwidget.maximize() + new_size, new_size_in_pixels = self.get_widget_sizes_from_context(iwidget) - self.assertEqual(old_size, new_size) - self.assertEqual(old_size_in_pixels, new_size_in_pixels) - self.assertEqual(old_affected_iwidget_position, affected_iwidget.layout_position) + self.assertEqual(old_size, new_size) + self.assertEqual(old_size_in_pixels, new_size_in_pixels) + self.assertEqual(old_affected_iwidget_position, affected_iwidget.layout_position) test_minimize_widget.tags = tags + ('wirecloud-dragboard',) @@ -1179,18 +1208,20 @@ def test_basic_layout_parameter_change(self): # Check initial sizes with widget: old_width_from_context = int(self.driver.find_element_by_css_selector('[data-name="width"] .content').text) + old_height_in_pixels_changes = int(self.driver.find_element_by_css_selector('[data-name="heightInPixels"] .badge').text) self.assertEqual(old_width_from_context, 6) # Change columns to 10 - self.open_menu().click_entry('Settings') - workspace_preferences_dialog = FormModalTester(self, self.wait_element_visible('.wc-workspace-preferences-modal')) + with self.edit_mode as edit_session: + self.open_menu().click_entry('Settings') + workspace_preferences_dialog = FormModalTester(self, self.wait_element_visible('.wc-workspace-preferences-modal')) - workspace_preferences_dialog.find_element('.fa-cogs').click() - layout_form = FormModalTester(self, self.wait_element_visible(".wc-layout-settings-modal")) - layout_form.get_field("columns").set_value('10') - layout_form.accept() + workspace_preferences_dialog.find_element('.fa-cogs').click() + layout_form = FormModalTester(self, self.wait_element_visible(".wc-layout-settings-modal")) + layout_form.get_field("columns").set_value('10') + layout_form.accept() - workspace_preferences_dialog.accept() + workspace_preferences_dialog.accept() # Check new sizes with widget: @@ -1205,7 +1236,7 @@ def test_basic_layout_parameter_change(self): width_in_pixels_changes = self.driver.find_element_by_css_selector('[data-name="widthInPixels"] .badge').text self.assertEqual(width_in_pixels_changes, '0') height_in_pixels_changes = self.driver.find_element_by_css_selector('[data-name="heightInPixels"] .badge').text - self.assertEqual(height_in_pixels_changes, '0') + self.assertEqual(height_in_pixels_changes, "%s" % (old_height_in_pixels_changes + 1)) test_basic_layout_parameter_change.tags = tags + ('wirecloud-dragboard',) @@ -1234,15 +1265,16 @@ def test_basic_layout_parameter_change_several_widgets(self): old_size_from_context2, old_size_in_pixels_from_context2 = self.get_widget_sizes_from_context(iwidgets[1]) # Change columns to 10 - self.open_menu().click_entry('Settings') - workspace_preferences_dialog = FormModalTester(self, self.wait_element_visible('.wc-workspace-preferences-modal')) + with self.edit_mode as edit_session: + self.open_menu().click_entry('Settings') + workspace_preferences_dialog = FormModalTester(self, self.wait_element_visible('.wc-workspace-preferences-modal')) - workspace_preferences_dialog.find_element('.fa-cogs').click() - layout_form = FormModalTester(self, self.wait_element_visible(".wc-layout-settings-modal")) - layout_form.get_field("columns").set_value('10') - layout_form.accept() + workspace_preferences_dialog.find_element('.fa-cogs').click() + layout_form = FormModalTester(self, self.wait_element_visible(".wc-layout-settings-modal")) + layout_form.get_field("columns").set_value('10') + layout_form.accept() - workspace_preferences_dialog.accept() + workspace_preferences_dialog.accept() # Check new widget 1 sizes new_size_from_context1, new_size_in_pixels_from_context1 = self.get_widget_sizes_from_context(iwidgets[0].wait_still()) @@ -1271,10 +1303,11 @@ def test_layout_type_change(self): self.assertEqual(old_size_from_context[0], 6) # Change current layout to grid - self.open_menu().click_entry('Settings') - form = FormModalTester(self, self.wait_element_visible(".wc-workspace-preferences-modal")) - form.get_field("baselayout-type").set_value('gridlayout') - form.accept() + with self.edit_mode as edit_session: + self.open_menu().click_entry('Settings') + form = FormModalTester(self, self.wait_element_visible(".wc-workspace-preferences-modal")) + form.get_field("baselayout-type").set_value('gridlayout') + form.accept() # Check new sizes new_size_from_context, new_size_in_pixels_from_context = self.get_widget_sizes_from_context(widget) @@ -1380,51 +1413,52 @@ def test_upgrade_widget(self): widget, other_widget = self.widgets - # Upgrade to version 3.0 - widget.open_menu().click_entry('Upgrade/Downgrade') - form = FormModalTester(self, self.wait_element_visible(".wc-upgrade-component-modal")) - form.accept() - - # Check settings - widget.open_menu().click_entry('Settings') - - form = FormModalTester(self, self.wait_element_visible(".wc-component-preferences-modal")) - self.assertRaises(NoSuchElementException, form.get_field, 'list') - self.assertEqual(form.get_field('text').value, 'initial text') - self.assertRaises(NoSuchElementException, form.get_field, 'boolean') - self.assertRaises(NoSuchElementException, form.get_field, 'number') - self.assertRaises(NoSuchElementException, form.get_field, 'password') - self.assertEqual(form.get_field('new').value, 'initial value') - form.accept() - - # Check wiring - self.send_basic_event(widget) - - # This should work as the outputendpoint is still available on version 3.0 - with other_widget: - WebDriverWait(self.driver, timeout=3).until(lambda driver: driver.find_element_by_id('wiringOut').text == 'hello world!!') - - self.send_basic_event(other_widget) - time.sleep(3) - - # Instead inputendpoint has been replaced by inputendpoint2 - with widget: - text_div = self.driver.find_element_by_id('wiringOut') - self.assertEqual(text_div.text, '') - - # Downgrade to version 1.0 - widget.open_menu().click_entry('Upgrade/Downgrade') - form = FormModalTester(self, self.wait_element_visible(".wc-upgrade-component-modal")) - form.accept() - - # Check settings - widget.open_menu().click_entry('Settings') - - form = FormModalTester(self, self.wait_element_visible(".wc-component-preferences-modal")) - self.assertEqual(form.get_field('list').value, 'default') - self.assertEqual(form.get_field('text').value, 'initial text') - self.assertRaises(NoSuchElementException, form.get_field, 'new') - form.accept() + with self.edit_mode as edit_session: + # Upgrade to version 3.0 + widget.open_menu().click_entry('Upgrade/Downgrade') + form = FormModalTester(self, self.wait_element_visible(".wc-upgrade-component-modal")) + form.accept() + + # Check settings + widget.open_menu().click_entry('Settings') + + form = FormModalTester(self, self.wait_element_visible(".wc-component-preferences-modal")) + self.assertRaises(NoSuchElementException, form.get_field, 'list') + self.assertEqual(form.get_field('text').value, 'initial text') + self.assertRaises(NoSuchElementException, form.get_field, 'boolean') + self.assertRaises(NoSuchElementException, form.get_field, 'number') + self.assertRaises(NoSuchElementException, form.get_field, 'password') + self.assertEqual(form.get_field('new').value, 'initial value') + form.accept() + + # Check wiring + self.send_basic_event(widget) + + # This should work as the outputendpoint is still available on version 3.0 + with other_widget: + WebDriverWait(self.driver, timeout=3).until(lambda driver: driver.find_element_by_id('wiringOut').text == 'hello world!!') + + self.send_basic_event(other_widget) + time.sleep(3) + + # Instead inputendpoint has been replaced by inputendpoint2 + with widget: + text_div = self.driver.find_element_by_id('wiringOut') + self.assertEqual(text_div.text, '') + + # Downgrade to version 1.0 + widget.open_menu().click_entry('Upgrade/Downgrade') + form = FormModalTester(self, self.wait_element_visible(".wc-upgrade-component-modal")) + form.accept() + + # Check settings + widget.open_menu().click_entry('Settings') + + form = FormModalTester(self, self.wait_element_visible(".wc-component-preferences-modal")) + self.assertEqual(form.get_field('list').value, 'default') + self.assertEqual(form.get_field('text').value, 'initial text') + self.assertRaises(NoSuchElementException, form.get_field, 'new') + form.accept() # Check wiring self.send_basic_event(widget, 'hello world 2!!') diff --git a/src/wirecloud/platform/wiring/tests.py b/src/wirecloud/platform/wiring/tests.py index 2fb9425361..f2ebf0dfd0 100644 --- a/src/wirecloud/platform/wiring/tests.py +++ b/src/wirecloud/platform/wiring/tests.py @@ -2143,105 +2143,110 @@ def _read_json_fixtures(self, *args): def test_component_dropped_out_of_bounds_should_be_added(self): self.login(username='user_with_workspaces', next='/user_with_workspaces/WiringTests') - with self.wiring_view as wiring: - with wiring.component_sidebar as sidebar: - self.assertIsNotNone(sidebar.add_component('operator', "Wirecloud/TestOperator", y=-20)) - self.assertIsNotNone(sidebar.add_component('widget', "Wirecloud/Test", title="Test (1)", x=-4)) + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: + with wiring.component_sidebar as sidebar: + self.assertIsNotNone(sidebar.add_component('operator', "Wirecloud/TestOperator", y=-20)) + self.assertIsNotNone(sidebar.add_component('widget', "Wirecloud/Test", title="Test (1)", x=-4)) test_component_dropped_out_of_bounds_should_be_added.tags = tags + ('wirecloud-wiring-draggable-component',) @uses_extra_resources(('Wirecloud_TestOperator_2.0.zip',), shared=True) def test_upgrade_operator(self): self.login(username='user_with_workspaces', next="/user_with_workspaces/Workspace") - with self.wiring_view as wiring: - with wiring.component_sidebar as sidebar: - operator = sidebar.find_component('operator', "Wirecloud/TestOperator", id=0) - operator.change_version("2.0") - modal = operator.show_logs() - WebDriverWait(self.driver, timeout=5).until(lambda driver: len(modal.find_alerts(title="The operator was upgraded to v2.0 successfully.")) == 1) - modal.accept() - draggable_operator = wiring.find_draggable_component('operator', id=operator.id) - - self.assertEqual(len(draggable_operator.find_endpoints('target')), 2) - target = draggable_operator.find_endpoint('target', "input") - self.assertFalse(target.has_class('missing')) - self.assertTrue(len(target.find_connections()), 1) - - self.assertEqual(len(draggable_operator.find_endpoints('source')), 2) - source = draggable_operator.find_endpoint('source', "output") - self.assertTrue(source.has_class('missing')) - self.assertTrue(len(source.find_connections()), 1) - - self.assertEqual(len(wiring.find_connections(extra_class="missing")), 1) - connection = wiring.find_connections(extra_class="missing")[0] - self.assertEqual(connection.source_id, source.id) + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: + with wiring.component_sidebar as sidebar: + operator = sidebar.find_component('operator', "Wirecloud/TestOperator", id=0) + operator.change_version("2.0") + modal = operator.show_logs() + WebDriverWait(self.driver, timeout=5).until(lambda driver: len(modal.find_alerts(title="The operator was upgraded to v2.0 successfully.")) == 1) + modal.accept() + draggable_operator = wiring.find_draggable_component('operator', id=operator.id) + + self.assertEqual(len(draggable_operator.find_endpoints('target')), 2) + target = draggable_operator.find_endpoint('target', "input") + self.assertFalse(target.has_class('missing')) + self.assertTrue(len(target.find_connections()), 1) + + self.assertEqual(len(draggable_operator.find_endpoints('source')), 2) + source = draggable_operator.find_endpoint('source', "output") + self.assertTrue(source.has_class('missing')) + self.assertTrue(len(source.find_connections()), 1) + + self.assertEqual(len(wiring.find_connections(extra_class="missing")), 1) + connection = wiring.find_connections(extra_class="missing")[0] + self.assertEqual(connection.source_id, source.id) @uses_extra_resources(('Wirecloud_Test_3.0.wgt',), shared=True) def test_upgrade_and_downgrade_widget(self): self.login(username='user_with_workspaces', next="/user_with_workspaces/Workspace") - with self.wiring_view as wiring: - # Upgrade to v3 on the sidebar - with wiring.component_sidebar as sidebar: - widget = sidebar.find_component('widget', "Wirecloud/Test", title="Test 1") - widget.change_version("3.0") - WebDriverWait(self.driver, timeout=3).until(lambda driver: widget.version == "v3.0") - modal = widget.show_logs() - WebDriverWait(self.driver, timeout=5).until(lambda driver: len(modal.find_alerts(title="The widget was upgraded to v3.0 successfully.")) == 1) + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: + # Upgrade to v3 on the sidebar + with wiring.component_sidebar as sidebar: + widget = sidebar.find_component('widget', "Wirecloud/Test", title="Test 1") + widget.change_version("3.0") + WebDriverWait(self.driver, timeout=3).until(lambda driver: widget.version == "v3.0") + modal = widget.show_logs() + WebDriverWait(self.driver, timeout=5).until(lambda driver: len(modal.find_alerts(title="The widget was upgraded to v3.0 successfully.")) == 1) + modal.accept() + + draggable_widget = wiring.find_draggable_component('widget', id=widget.id) + + self.assertEqual(len(draggable_widget.find_endpoints('target')), 2) + target = draggable_widget.find_endpoint('target', "inputendpoint") + self.assertTrue(target.has_class('missing')) + self.assertTrue(len(target.find_connections()), 1) + + self.assertEqual(len(wiring.find_connections(extra_class="missing")), 1) + connection = wiring.find_connections(extra_class="missing")[0] + self.assertEqual(connection.target_id, target.id) + + self.assertEqual(len(draggable_widget.find_endpoints('source')), 1) + source = draggable_widget.find_endpoint('source', "outputendpoint") + self.assertFalse(source.has_class('missing')) + self.assertTrue(len(source.find_connections()), 1) + + # Downgrade to v1 using the widget preferences + draggable_widget.change_version("1.0") + modal = draggable_widget.show_logs() + WebDriverWait(self.driver, timeout=5).until(lambda driver: len(modal.find_alerts(title="The widget was downgraded to v1.0 successfully.")) == 1) modal.accept() - draggable_widget = wiring.find_draggable_component('widget', id=widget.id) - - self.assertEqual(len(draggable_widget.find_endpoints('target')), 2) - target = draggable_widget.find_endpoint('target', "inputendpoint") - self.assertTrue(target.has_class('missing')) - self.assertTrue(len(target.find_connections()), 1) - - self.assertEqual(len(wiring.find_connections(extra_class="missing")), 1) - connection = wiring.find_connections(extra_class="missing")[0] - self.assertEqual(connection.target_id, target.id) - - self.assertEqual(len(draggable_widget.find_endpoints('source')), 1) - source = draggable_widget.find_endpoint('source', "outputendpoint") - self.assertFalse(source.has_class('missing')) - self.assertTrue(len(source.find_connections()), 1) - - # Downgrade to v1 using the widget preferences - draggable_widget.change_version("1.0") - modal = draggable_widget.show_logs() - WebDriverWait(self.driver, timeout=5).until(lambda driver: len(modal.find_alerts(title="The widget was downgraded to v1.0 successfully.")) == 1) - modal.accept() - - self.assertEqual(len(draggable_widget.find_endpoints('target')), 2) - target = draggable_widget.find_endpoint('target', "inputendpoint") - self.assertFalse(target.has_class('missing')) - self.assertTrue(len(target.find_connections()), 1) + self.assertEqual(len(draggable_widget.find_endpoints('target')), 2) + target = draggable_widget.find_endpoint('target', "inputendpoint") + self.assertFalse(target.has_class('missing')) + self.assertTrue(len(target.find_connections()), 1) - self.assertEqual(len(wiring.find_connections(extra_class="missing")), 0) + self.assertEqual(len(wiring.find_connections(extra_class="missing")), 0) @uses_extra_resources(('Wirecloud_Test_3.0.wgt',), shared=True) def test_remove_missing_endpoint_with_no_connections(self): self.login(username='user_with_workspaces', next="/user_with_workspaces/Workspace") - with self.wiring_view as wiring: - draggable_widget = wiring.find_draggable_component('widget', title="Test 1") - draggable_widget.change_version("3.0") + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: + draggable_widget = wiring.find_draggable_component('widget', title="Test 1") + draggable_widget.change_version("3.0") - WebDriverWait(self.driver, timeout=5).until(lambda driver: len(wiring.find_connections(extra_class="missing")) == 1) + WebDriverWait(self.driver, timeout=5).until(lambda driver: len(wiring.find_connections(extra_class="missing")) == 1) - connection = wiring.find_connections(extra_class="missing")[0] - connection.remove() + connection = wiring.find_connections(extra_class="missing")[0] + connection.remove() - self.assertIsNone(draggable_widget.find_endpoint('target', "inputendpoint")) + self.assertIsNone(draggable_widget.find_endpoint('target', "inputendpoint")) @uses_extra_resources(('Wirecloud_Test_3.0.wgt',), shared=True) def test_missing_endpoints_cannot_be_ordered(self): self.login(username='user_with_workspaces', next="/user_with_workspaces/Workspace") - with self.wiring_view as wiring: - draggable_widget = wiring.find_draggable_component('widget', title="Test 1") - draggable_widget.change_version("3.0") - draggable_widget.show_preferences().check(must_be_disabled=("Order endpoints",)) + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: + draggable_widget = wiring.find_draggable_component('widget', title="Test 1") + draggable_widget.change_version("3.0") + draggable_widget.show_preferences().check(must_be_disabled=("Order endpoints",)) def test_widget_uninstalled_with_tradeinfo(self): @@ -2249,8 +2254,9 @@ def test_widget_uninstalled_with_tradeinfo(self): self.login(username='user_with_workspaces', next="/user_with_workspaces/Workspace") - with self.wiring_view as wiring: - self.assertEqual(len(wiring.find_draggable_components('widget', extra_class='missing')), 2) + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: + self.assertEqual(len(wiring.find_draggable_components('widget', extra_class='missing')), 2) def test_widget_with_visualinfo_and_connections_is_not_in_workspace(self): @@ -2267,9 +2273,10 @@ def test_widget_with_visualinfo_and_connections_is_not_in_workspace(self): # status self.assertIsNone(self.find_navbar_button("wc-show-wiring-button").badge) - with self.wiring_view as wiring: - # Check the Wiring Editor only display the valid widgets - self.assertEqual(len(wiring.find_draggable_components('widget')), 2) + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: + # Check the Wiring Editor only display the valid widgets + self.assertEqual(len(wiring.find_draggable_components('widget')), 2) def _check_operator_missing(self, wiring, component_id, source_length, target_length): operator = wiring.find_draggable_component('operator', id=component_id) @@ -2288,11 +2295,12 @@ def test_operator_uninstalled_with_tradeinfo(self): self.login(username='user_with_workspaces', next='/user_with_workspaces/Workspace') self.check_wiring_badge("1") - with self.wiring_view as wiring: - self._check_operator_missing(wiring, 1, 0, 0) + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: + self._check_operator_missing(wiring, 1, 0, 0) - with self.wiring_view as wiring: - self._check_operator_missing(wiring, 1, 0, 0) + with edit_session.wiring_view as wiring: + self._check_operator_missing(wiring, 1, 0, 0) def test_operator_uninstalled_with_tradeinfo_and_connections(self): workspace = Workspace.objects.get(id=2) @@ -2302,9 +2310,10 @@ def test_operator_uninstalled_with_tradeinfo_and_connections(self): self.login(username='user_with_workspaces', next="/user_with_workspaces/Workspace") self.check_wiring_badge("4") - with self.wiring_view as wiring: - self._check_operator_missing(wiring, 1, 1, 1) - self.assertEqual(len(wiring.find_connections(extra_class="missing")), 3) + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: + self._check_operator_missing(wiring, 1, 1, 1) + self.assertEqual(len(wiring.find_connections(extra_class="missing")), 3) @uses_extra_resources(('Wirecloud_TestOperator_2.0.zip',), shared=True) def test_upgrade_missing_operator(self): @@ -2314,15 +2323,16 @@ def test_upgrade_missing_operator(self): self.login(username='user_with_workspaces', next="/user_with_workspaces/Workspace") - with self.wiring_view as wiring: + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: - # Check operator is marked as missing - operator = wiring.find_draggable_component('operator', id=0) - self.assertTrue(operator.has_class('missing')) + # Check operator is marked as missing + operator = wiring.find_draggable_component('operator', id=0) + self.assertTrue(operator.has_class('missing')) - # Upgrade it to version 2.0 and check it leaves the missing status - operator.change_version("2.0") - WebDriverWait(self.driver, timeout=5).until(lambda driver: not operator.has_class('missing')) + # Upgrade it to version 2.0 and check it leaves the missing status + operator.change_version("2.0") + WebDriverWait(self.driver, timeout=5).until(lambda driver: not operator.has_class('missing')) def test_operator_install_uninstall(self): self.login(username='user_with_workspaces', next='/user_with_workspaces/pending-events') @@ -2330,18 +2340,20 @@ def test_operator_install_uninstall(self): with self.myresources_view as myresources: myresources.upload_resource('Wirecloud_TestOperatorSelenium_1.0.zip', 'TestOperatorSelenium', shared=True) - with self.wiring_view as wiring: - with wiring.component_sidebar as sidebar: - self.assertIsNotNone(sidebar.find_component_group('operator', "Wirecloud/TestOperatorSelenium")) + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: + with wiring.component_sidebar as sidebar: + self.assertIsNotNone(sidebar.find_component_group('operator', "Wirecloud/TestOperatorSelenium")) with self.myresources_view as myresources: myresources.uninstall_resource("TestOperator") - with self.wiring_view as wiring: - self.assertTrue(wiring.find_draggable_component('operator', id=0).has_class('missing')) + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: + self.assertTrue(wiring.find_draggable_component('operator', id=0).has_class('missing')) - with wiring.component_sidebar as sidebar: - self.assertIsNone(sidebar.find_component_group('operator', "Wirecloud/TestOperator")) + with wiring.component_sidebar as sidebar: + self.assertIsNone(sidebar.find_component_group('operator', "Wirecloud/TestOperator")) test_operator_install_uninstall.tags = tags + ('wirecloud-wiring-components',) def test_operator_not_usable_after_being_deleted(self): @@ -2350,9 +2362,10 @@ def test_operator_not_usable_after_being_deleted(self): with self.myresources_view as myresources: myresources.delete_resource('TestOperator') - with self.wiring_view as wiring: - with wiring.component_sidebar as sidebar: - self.assertIsNone(sidebar.find_component_group('operator', "Wirecloud/TestOperator")) + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: + with wiring.component_sidebar as sidebar: + self.assertIsNone(sidebar.find_component_group('operator', "Wirecloud/TestOperator")) test_operator_not_usable_after_being_deleted.tags = tags + ('wirecloud-wiring-components',) @uses_extra_resources(('Wirecloud_Test_NoImage_3.0.wgt',), shared=True) @@ -2360,10 +2373,11 @@ def test_widget_with_no_image(self): # Add a widget with no image included. self.login(username="admin", next="admin/Workspace") - with self.wiring_view as wiring: - with wiring.component_sidebar as sidebar: - # The current version displayed should show the default no-image. - self.assertFalse(sidebar.find_component_group('widget', "Wirecloud/Test_NoImage").has_image()) + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: + with wiring.component_sidebar as sidebar: + # The current version displayed should show the default no-image. + self.assertFalse(sidebar.find_component_group('widget', "Wirecloud/Test_NoImage").has_image()) test_widget_with_no_image.tags = tags + ('wirecloud-wiring-components',) def test_operator_can_be_used_after_being_reinstalled(self): @@ -2420,12 +2434,13 @@ def _check_reinstalled_operator(self, prefix): WebDriverWait(self.driver, timeout=5).until(lambda driver: driver.find_element_by_id('wiringOut').text == prefix + event) # Check preference values has been restored to the values used before uninstalling the widget and not to the default ones - with self.wiring_view as wiring: - modal = wiring.find_draggable_component('operator', id=0).show_settings() - self.assertEqual(modal.get_field('prefix').value, "test_") - self.assertFalse(modal.get_field('exception_on_event').is_selected) - self.assertTrue(modal.get_field('test_logging').is_selected) - modal.accept() + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: + modal = wiring.find_draggable_component('operator', id=0).show_settings() + self.assertEqual(modal.get_field('prefix').value, "test_") + self.assertFalse(modal.get_field('exception_on_event').is_selected) + self.assertTrue(modal.get_field('test_logging').is_selected) + modal.accept() @uses_extra_resources(('Wirecloud_api-test_0.9.wgt',), shared=True) @uses_extra_workspace('admin', 'Wirecloud_api-test-mashup_1.0.wgt', shared=True) @@ -2455,19 +2470,20 @@ def test_dashboard_management_api_support(self): # the connection between the volatile operator and the volatile widget WebDriverWait(self.driver, timeout=5).until(lambda driver: len(self.widgets) == (iwidgets_count + 2)) - with self.wiring_view as wiring: - self.assertEqual(len(wiring.find_draggable_components('operator')), 1) - self.assertEqual(len(wiring.find_draggable_components('widget')), 3) + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: + self.assertEqual(len(wiring.find_draggable_components('operator')), 1) + self.assertEqual(len(wiring.find_draggable_components('widget')), 3) - with wiring.component_sidebar as sidebar: - # The dasboard management test creates a volatile operator, - self.assertEqual(len(sidebar.find_components('operator', "Wirecloud/TestOperator", state='volatile')), 1) - # two volatile widgets - self.assertEqual(len(sidebar.find_components('widget', "Wirecloud/api-test", state='volatile')), 2) + with wiring.component_sidebar as sidebar: + # The dasboard management test creates a volatile operator, + self.assertEqual(len(sidebar.find_components('operator', "Wirecloud/TestOperator", state='volatile')), 1) + # two volatile widgets + self.assertEqual(len(sidebar.find_components('widget', "Wirecloud/api-test", state='volatile')), 2) - # and a volatile connection between the operator and one the volatile widgets - # Wiring Editor should only display the initial connections - self.assertEqual(len(wiring.find_connections()), 2) + # and a volatile connection between the operator and one the volatile widgets + # Wiring Editor should only display the initial connections + self.assertEqual(len(wiring.find_connections()), 2) # Check dynamic connections created by the dashboard_management_button works as expected with iwidgets[1]: @@ -2508,13 +2524,14 @@ def test_connections_works_engine_disabled(self): tab_widget1, tab_widget2, _ = self.widgets # Create a new connection and check it works - with self.wiring_view as wiring: - widget1 = wiring.find_draggable_component('widget', id=tab_widget1.id) - widget2 = wiring.find_draggable_component('widget', id=tab_widget2.id) + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: + widget1 = wiring.find_draggable_component('widget', id=tab_widget1.id) + widget2 = wiring.find_draggable_component('widget', id=tab_widget2.id) - source = widget1.find_endpoint('source', "outputendpoint") - target = widget2.find_endpoint('target', "inputendpoint") - source.create_connection(target) + source = widget1.find_endpoint('source', "outputendpoint") + target = widget2.find_endpoint('target', "inputendpoint") + source.create_connection(target) self.send_basic_event(tab_widget1, event) @@ -2525,9 +2542,10 @@ def test_connections_works_engine_disabled(self): ) # Now remove the connection - with self.wiring_view as wiring: - for connection in wiring.find_connections(): - connection.remove() + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: + for connection in wiring.find_connections(): + connection.remove() self.send_basic_event(tab_widget1, "other") # Wait 5 seconds before checking no event is received @@ -2539,23 +2557,25 @@ def test_connections_works_engine_disabled(self): def test_component_preferences_in_wiring_editor(self): self.login(username='user_with_workspaces', next="/user_with_workspaces/Workspace") - tab_widget = self.find_widget(title="Test 1") pref_prefix = "prefix: " - with self.wiring_view as wiring: - widget = wiring.find_draggable_component('widget', id=tab_widget.id) - operator = wiring.find_draggable_component('operator', id=0) - operator.wait_to_be_loaded() + with self.edit_mode as edit_session: + tab_widget = self.find_widget(title="Test 1") + + with edit_session.wiring_view as wiring: + widget = wiring.find_draggable_component('widget', id=tab_widget.id) + operator = wiring.find_draggable_component('operator', id=0) + operator.wait_to_be_loaded() - # Update widget preferences using the wiring editor interface - modal = widget.show_settings() - modal.get_field('text').set_value("test") - modal.accept() + # Update widget preferences using the wiring editor interface + modal = widget.show_settings() + modal.get_field('text').set_value("test") + modal.accept() - # Update operator preferences using the wiring editor interface - modal = operator.show_settings() - modal.get_field('prefix').set_value(pref_prefix) - modal.accept() + # Update operator preferences using the wiring editor interface + modal = operator.show_settings() + modal.get_field('prefix').set_value(pref_prefix) + modal.accept() # Check the widget has received an event with the new values with tab_widget: @@ -2596,15 +2616,16 @@ def test_wiring_editor_create_and_modify_connection_endpoints(self): # Modify connection between widget1 and widget2 # so widget1 is connected to widget3 instead - with self.wiring_view as wiring: - widget2 = wiring.find_draggable_component('widget', title="Test (2)") - widget3 = wiring.find_draggable_component('widget', title="Test (3)") + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: + widget2 = wiring.find_draggable_component('widget', title="Test (2)") + widget3 = wiring.find_draggable_component('widget', title="Test (3)") - old_target = widget2.find_endpoint('target', "inputendpoint") - new_target = widget3.find_endpoint('target', "inputendpoint") + old_target = widget2.find_endpoint('target', "inputendpoint") + new_target = widget3.find_endpoint('target', "inputendpoint") - connection = wiring.find_connection("widget/7/outputendpoint", "widget/8/inputendpoint") - connection.change_endpoint(old_target, new_target) + connection = wiring.find_connection("widget/7/outputendpoint", "widget/8/inputendpoint") + connection.change_endpoint(old_target, new_target) # Check new wiring configuration self.send_basic_event(widgets[0], event2) @@ -2619,81 +2640,85 @@ def test_operator_logging_support(self): self.login(username='user_with_workspaces', next="/user_with_workspaces/Workspace") - with self.wiring_view as wiring: - operator = wiring.find_draggable_component('operator', id=0) - operator.wait_to_be_loaded() + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: + operator = wiring.find_draggable_component('operator', id=0) + operator.wait_to_be_loaded() - modal = operator.show_logs() - self.assertEqual(len(modal.find_alerts(state='error')), 0) - modal.accept() + modal = operator.show_logs() + self.assertEqual(len(modal.find_alerts(state='error')), 0) + modal.accept() - # Make test operator log some errors - modal = operator.show_settings() - modal.get_field('test_logging').click() - modal.accept() + # Make test operator log some errors + modal = operator.show_settings() + modal.get_field('test_logging').click() + modal.accept() - # Check operator registered correctly the errors raised by the operator - modal = operator.show_logs() - self.assertEqual(len(modal.find_alerts(state='error')), 2) - modal.accept() + # Check operator registered correctly the errors raised by the operator + modal = operator.show_logs() + self.assertEqual(len(modal.find_alerts(state='error')), 2) + modal.accept() - with self.find_widget(title="Test 1"): - self.assertEqual(self.driver.find_element_by_id('wiringOut').text, 'preferences changed: test_logging') + with self.find_widget(title="Test 1"): + self.assertEqual(self.driver.find_element_by_id('wiringOut').text, 'preferences changed: test_logging') @uses_extra_resources(('Wirecloud_api-test_0.9.wgt',), shared=True) @uses_extra_workspace('admin', 'Wirecloud_api-test-mashup_1.0.wgt', shared=True) def test_wiring_status_change_events_from_components(self): self.login(username="admin", next="admin/api-test-mashup") - tab_widget = self.find_widget(title="Wirecloud API test") - with tab_widget: - self.assertEqual(self.driver.find_element_by_id('wiring_hasinputconnections_test').text, "true") - self.assertEqual(self.driver.find_element_by_id('wiring_hasoutputconnections_test').text, "false") + with self.edit_mode as edit_session: + tab_widget = self.find_widget(title="Wirecloud API test") - with self.wiring_view as wiring: + with tab_widget: + self.assertEqual(self.driver.find_element_by_id('wiring_hasinputconnections_test').text, "true") + self.assertEqual(self.driver.find_element_by_id('wiring_hasoutputconnections_test').text, "false") - # Make modifications into the wiring - for connection in wiring.find_connections(): - connection.remove() + with edit_session.wiring_view as wiring: - widget = wiring.find_draggable_component('widget', id=tab_widget.id) - operator = wiring.find_draggable_component('operator', id=1) + # Make modifications into the wiring + for connection in wiring.find_connections(): + connection.remove() - target = operator.find_endpoint('target', "input") - source = widget.find_endpoint('source', "outputendpoint") - source.create_connection(target) + widget = wiring.find_draggable_component('widget', id=tab_widget.id) + operator = wiring.find_draggable_component('operator', id=1) - # Create another connection between the operator and the output widget - widget2 = wiring.find_draggable_component('widget', title="Test (connected to the test operator)") + target = operator.find_endpoint('target', "input") + source = widget.find_endpoint('source', "outputendpoint") + source.create_connection(target) - source = operator.find_endpoint('source', "output") - target = widget2.find_endpoint('target', "inputendpoint") - source.create_connection(target) + # Create another connection between the operator and the output widget + widget2 = wiring.find_draggable_component('widget', title="Test (connected to the test operator)") - with tab_widget: - self.assertEqual(self.driver.find_element_by_id('wiring_hasinputconnections_test').text, "false") - self.assertEqual(self.driver.find_element_by_id('wiring_hasoutputconnections_test').text, "true") + source = operator.find_endpoint('source', "output") + target = widget2.find_endpoint('target', "inputendpoint") + source.create_connection(target) - # The operator automatically sends a "wiring modified" event when it detects a wiring change - with self.find_widget(title="Test (connected to the test operator)"): - element = self.driver.find_element_by_id('wiringOut') - WebDriverWait(self.driver, timeout=2).until( - lambda driver: element.text == "wiring modified" - ) + with tab_widget: + self.assertEqual(self.driver.find_element_by_id('wiring_hasinputconnections_test').text, "false") + self.assertEqual(self.driver.find_element_by_id('wiring_hasoutputconnections_test').text, "true") + + # The operator automatically sends a "wiring modified" event when it detects a wiring change + with self.find_widget(title="Test (connected to the test operator)"): + element = self.driver.find_element_by_id('wiringOut') + WebDriverWait(self.driver, timeout=2).until( + lambda driver: element.text == "wiring modified" + ) def test_remove_components_with_endpoint_attached_to_more_than_one_connection(self): self.login(username='user_with_workspaces', next="/user_with_workspaces/Workspace") - with self.wiring_view as wiring: - # Ready an endpoint with more than one connection - operator = wiring.find_draggable_component('operator', id=0) - target = operator.find_endpoint('source', "output") - source = wiring.find_draggable_component('widget', title="Test 1").find_endpoint('target', "nothandled") - source.create_connection(target) - self.assertEqual(len(target.find_connections()), 2) - - operator.remove() - self.assertEqual(len(wiring.find_connections()), 1) + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: + # Ready an endpoint with more than one connection + operator = wiring.find_draggable_component('operator', id=0) + target = operator.find_endpoint('source', "output") + source = wiring.find_draggable_component('widget', title="Test 1").find_endpoint('target', "nothandled") + source.create_connection(target) + self.assertEqual(len(target.find_connections()), 2) + + operator.remove() + self.assertEqual(len(wiring.find_connections()), 1) test_remove_components_with_endpoint_attached_to_more_than_one_connection.tags = tags + ('wirecloud-wiring-draggable-component',) @uses_extra_workspace('user_with_workspaces', 'Wirecloud_mashup-with-behaviours_1.0.wgt', shared=True) @@ -2711,57 +2736,59 @@ def test_remove_components_using_key_delete_when_behaviour_engine_is_enabled(sel self.login(username='user_with_workspaces', next='/user_with_workspaces/mashup-with-behaviours') - with self.wiring_view as wiring: + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: - # Select the components using the command key - widget1 = wiring.find_draggable_component('widget', title="Test 1") - widget2 = wiring.find_draggable_component('widget', title="Test 2") - operator = wiring.find_draggable_component('operator', id=1) + # Select the components using the command key + widget1 = wiring.find_draggable_component('widget', title="Test 1") + widget2 = wiring.find_draggable_component('widget', title="Test 2") + operator = wiring.find_draggable_component('operator', id=1) - wiring.select(components=(widget1, widget2, operator), key=Keys.COMMAND) + wiring.select(components=(widget1, widget2, operator), key=Keys.COMMAND) - # Remove the selection using the backspace key - send_basic_key_event(self.driver, 8) + # Remove the selection using the backspace key + send_basic_key_event(self.driver, 8) - modal = FormModalTester(self, self.wait_element_visible(".wc-alert-modal")) - self.assertIn('Test 2', modal.body.text) - self.assertNotIn('Test 1', modal.body.text) - self.assertNotIn('TestOperator', modal.body.text) - modal.accept() + modal = FormModalTester(self, self.wait_element_visible(".wc-alert-modal")) + self.assertIn('Test 2', modal.body.text) + self.assertNotIn('Test 1', modal.body.text) + self.assertNotIn('TestOperator', modal.body.text) + modal.accept() - self.assertIsNone(wiring.find_draggable_component('widget', title="Test 2")) - self.assertTrue(wiring.find_draggable_component('operator', id=1).has_class('background')) - self.assertTrue(wiring.find_draggable_component('widget', title="Test 1").has_class('background')) + self.assertIsNone(wiring.find_draggable_component('widget', title="Test 2")) + self.assertTrue(wiring.find_draggable_component('operator', id=1).has_class('background')) + self.assertTrue(wiring.find_draggable_component('widget', title="Test 1").has_class('background')) test_remove_components_using_key_delete_when_behaviour_engine_is_enabled.tags = tags + ('wirecloud-wiring-draggable-component',) def test_rename_widget_from_component_preferences(self): new_title = "New title" self.login(username='user_with_workspaces', next="/user_with_workspaces/Workspace") - with self.wiring_view as wiring: - with wiring.component_sidebar as sidebar: - # Rename the widget available in sidebar. - component = sidebar.find_component('widget', "Wirecloud/Test", title="Test 1") - component.scroll().rename(new_title) - # Check if the widget draggable's title is changed too. - self.assertIsNotNone(wiring.find_draggable_component('widget', title=new_title)) - # Check if the widget interface's title is changed too. - self.assertIsNotNone(self.find_widget(title=new_title)) - - # - # Now rename the widget from the draggable component instead - # - with self.wiring_view as wiring: - # Rename the widget draggable available in wiring diagram. - component = wiring.find_draggable_component('widget', title=new_title) - new_title = "Other Name" - component.rename(new_title) - - with wiring.component_sidebar as sidebar: - # Check if the widget 's title of in sidebar is changed too. - self.assertIsNotNone(sidebar.find_component('widget', "Wirecloud/Test", title=new_title)) - # Check if the widget interface's title has changed too - self.assertIsNotNone(self.find_widget(title=new_title)) + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: + with wiring.component_sidebar as sidebar: + # Rename the widget available in sidebar. + component = sidebar.find_component('widget', "Wirecloud/Test", title="Test 1") + component.scroll().rename(new_title) + # Check if the widget draggable's title is changed too. + self.assertIsNotNone(wiring.find_draggable_component('widget', title=new_title)) + # Check if the widget interface's title is changed too. + self.assertIsNotNone(self.find_widget(title=new_title)) + + # + # Now rename the widget from the draggable component instead + # + with edit_session.wiring_view as wiring: + # Rename the widget draggable available in wiring diagram. + component = wiring.find_draggable_component('widget', title=new_title) + new_title = "Other Name" + component.rename(new_title) + + with wiring.component_sidebar as sidebar: + # Check if the widget 's title of in sidebar is changed too. + self.assertIsNotNone(sidebar.find_component('widget', "Wirecloud/Test", title=new_title)) + # Check if the widget interface's title has changed too + self.assertIsNotNone(self.find_widget(title=new_title)) test_rename_widget_from_component_preferences.tags = tags + ('wirecloud-wiring-draggable-component',) def test_components_with_readonly_connections_cannot_be_deleted(self): @@ -2772,114 +2799,118 @@ def test_components_with_readonly_connections_cannot_be_deleted(self): self.login(username='user_with_workspaces', next="/user_with_workspaces/Workspace") - with self.wiring_view as wiring: - # Such connection should be readonly and its button 'delete' - # should be disabled - connection = wiring.find_connection("widget/2/outputendpoint", "operator/0/input") - self.assertTrue(connection.has_class('readonly')) - self.assertTrue(connection.btn_remove.is_disabled) - - # Both components of the readonly connection should also be readonly and - # their buttons 'delete' should be disabled - widget = wiring.find_draggable_component('widget', id=2) - self.assertTrue(widget.has_class('readonly')) - self.assertTrue(widget.btn_remove.is_disabled) - operator = wiring.find_draggable_component('operator', id=0) - self.assertTrue(operator.has_class('readonly')) - self.assertTrue(operator.btn_remove.is_disabled) + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: + # Such connection should be readonly and its button 'delete' + # should be disabled + connection = wiring.find_connection("widget/2/outputendpoint", "operator/0/input") + self.assertTrue(connection.has_class('readonly')) + self.assertTrue(connection.btn_remove.is_disabled) + + # Both components of the readonly connection should also be readonly and + # their buttons 'delete' should be disabled + widget = wiring.find_draggable_component('widget', id=2) + self.assertTrue(widget.has_class('readonly')) + self.assertTrue(widget.btn_remove.is_disabled) + operator = wiring.find_draggable_component('operator', id=0) + self.assertTrue(operator.has_class('readonly')) + self.assertTrue(operator.btn_remove.is_disabled) test_components_with_readonly_connections_cannot_be_deleted.tags = tags + ('wirecloud-wiring-connection-management',) def test_modify_connection_on_active_behaviours(self): self.login(username='user_with_workspaces', next='/user_with_workspaces/WorkspaceBehaviours') - with self.wiring_view as wiring: - operator = wiring.find_draggable_component('operator', id=0) - target1 = operator.find_endpoint('target', 'input') - target2 = operator.find_endpoint('target', 'nothandled') - - # The button 'cancel' corresponds to modify such connection only in the active behaviour - connection = wiring.find_connection('widget/11/outputendpoint', 'operator/0/input') - connection.change_endpoint(target1, target2) - modal = FormModalTester(self, self.wait_element_visible(".wc-alert-modal")) - modal.cancel() - - self.assertTrue(wiring.find_connection('widget/11/outputendpoint', 'operator/0/input').has_class('background')) - connection = wiring.find_connection('widget/11/outputendpoint', 'operator/0/nothandled') - self.assertIsNotNone(connection) - self.assertTrue(connection.has_class('active')) - - # Connections on the background cannot be modified - # In this case, a new connection is created - source = operator.find_endpoint('source', 'output') - - widget = wiring.find_draggable_component('widget', title="Test 1") - target = widget.find_endpoint('target', 'nothandled') - - connection1 = wiring.find_connection('operator/0/output', 'widget/10/inputendpoint') - # Remove connection1 from the current behaviour (convert into a - # background connection) - connection1.remove().click() - connection2 = source.create_connection(target) - - self.assertTrue(connection1.has_class('background')) - self.assertFalse(connection1.has_class('active')) - self.assertIsNotNone(connection2) + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: + operator = wiring.find_draggable_component('operator', id=0) + target1 = operator.find_endpoint('target', 'input') + target2 = operator.find_endpoint('target', 'nothandled') + + # The button 'cancel' corresponds to modify such connection only in the active behaviour + connection = wiring.find_connection('widget/11/outputendpoint', 'operator/0/input') + connection.change_endpoint(target1, target2) + modal = FormModalTester(self, self.wait_element_visible(".wc-alert-modal")) + modal.cancel() + + self.assertTrue(wiring.find_connection('widget/11/outputendpoint', 'operator/0/input').has_class('background')) + connection = wiring.find_connection('widget/11/outputendpoint', 'operator/0/nothandled') + self.assertIsNotNone(connection) + self.assertTrue(connection.has_class('active')) + + # Connections on the background cannot be modified + # In this case, a new connection is created + source = operator.find_endpoint('source', 'output') + + widget = wiring.find_draggable_component('widget', title="Test 1") + target = widget.find_endpoint('target', 'nothandled') + + connection1 = wiring.find_connection('operator/0/output', 'widget/10/inputendpoint') + # Remove connection1 from the current behaviour (convert into a + # background connection) + connection1.remove().click() + connection2 = source.create_connection(target) + + self.assertTrue(connection1.has_class('background')) + self.assertFalse(connection1.has_class('active')) + self.assertIsNotNone(connection2) test_modify_connection_on_active_behaviours.tags = tags + ('wirecloud-wiring-connection-management',) @uses_extra_workspace('user_with_workspaces', 'Wirecloud_test-mashup-recommendations_1.0.wgt', shared=True) def test_endpoints_are_recommended(self): self.login(username='user_with_workspaces', next='/user_with_workspaces/test-mashup-recommendations') - with self.wiring_view as wiring: - # Check endpoints are recommended on mouse over - widget1 = wiring.find_draggable_component('widget', id=self.widgets[0].id) - widget2 = wiring.find_draggable_component('widget', id=self.widgets[1].id) - widget3 = wiring.find_draggable_component('widget', id=self.widgets[2].id) + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: + # Check endpoints are recommended on mouse over + widget1 = wiring.find_draggable_component('widget', id=self.widgets[0].id) + widget2 = wiring.find_draggable_component('widget', id=self.widgets[1].id) + widget3 = wiring.find_draggable_component('widget', id=self.widgets[2].id) - target1 = widget1.find_endpoint('target', 'inputendpoint') - source2 = widget2.find_endpoint('source', 'outputendpoint') - target3 = widget3.find_endpoint('target', 'inputendpoint') - source3 = widget3.find_endpoint('source', 'outputendpoint') + target1 = widget1.find_endpoint('target', 'inputendpoint') + source2 = widget2.find_endpoint('source', 'outputendpoint') + target3 = widget3.find_endpoint('target', 'inputendpoint') + source3 = widget3.find_endpoint('source', 'outputendpoint') - source2.mouse_over(must_recommend=(target1, target3)) - target1.mouse_over(must_recommend=(source2, source3)) + source2.mouse_over(must_recommend=(target1, target3)) + target1.mouse_over(must_recommend=(source2, source3)) - # Check endpoints are recommended on connection creation - source2.create_connection(target3, must_recommend=(target1, target3)) - target1.create_connection(source3, must_recommend=(source2, source3)) + # Check endpoints are recommended on connection creation + source2.create_connection(target3, must_recommend=(target1, target3)) + target1.create_connection(source3, must_recommend=(source2, source3)) test_endpoints_are_recommended.tags = tags + ('wirecloud-wiring-endpoint-management',) def test_components_can_be_collapsed_and_expanded(self): self.login(username='user_with_workspaces', next='/user_with_workspaces/Workspace') # Collapse a widget and an operator - with self.wiring_view as wiring: - with wiring.component_sidebar as sidebar: - operators = sidebar.find_components('operator', 'Wirecloud/TestOperator', state='in use') - widgets = sidebar.find_components('widget', 'Wirecloud/Test', state='in use') + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: + with wiring.component_sidebar as sidebar: + operators = sidebar.find_components('operator', 'Wirecloud/TestOperator', state='in use') + widgets = sidebar.find_components('widget', 'Wirecloud/Test', state='in use') - operator = wiring.find_draggable_component('operator', id=operators[0].id) - operator.collapse_endpoints() + operator = wiring.find_draggable_component('operator', id=operators[0].id) + operator.collapse_endpoints() - widget1 = wiring.find_draggable_component('widget', id=widgets[0].id) - widget1.collapse_endpoints() + widget1 = wiring.find_draggable_component('widget', id=widgets[0].id) + widget1.collapse_endpoints() - # Check the components are expanded while creating a connection - widget2 = wiring.find_draggable_component('widget', id=widgets[1].id) + # Check the components are expanded while creating a connection + widget2 = wiring.find_draggable_component('widget', id=widgets[1].id) - source = widget2.find_endpoint('source', "outputendpoint") - target = widget1.find_endpoint('target', "inputendpoint") - source.create_connection(target, must_expand=(operator, widget1)) + source = widget2.find_endpoint('source', "outputendpoint") + target = widget1.find_endpoint('target', "inputendpoint") + source.create_connection(target, must_expand=(operator, widget1)) - # Expand the components - with self.wiring_view as wiring: - with wiring.component_sidebar as sidebar: - operators = sidebar.find_components('operator', 'Wirecloud/TestOperator', state='in use') - widgets = sidebar.find_components('widget', 'Wirecloud/Test', state='in use') + # Expand the components + with edit_session.wiring_view as wiring: + with wiring.component_sidebar as sidebar: + operators = sidebar.find_components('operator', 'Wirecloud/TestOperator', state='in use') + widgets = sidebar.find_components('widget', 'Wirecloud/Test', state='in use') - wiring.find_draggable_component('operator', id=operators[0].id).expand_endpoints() - wiring.find_draggable_component('widget', id=widgets[0].id).expand_endpoints() + wiring.find_draggable_component('operator', id=operators[0].id).expand_endpoints() + wiring.find_draggable_component('widget', id=widgets[0].id).expand_endpoints() test_components_can_be_collapsed_and_expanded.tags = tags + ('wirecloud-wiring-endpoint-management',) def check_input_endpoint_exceptions(self): @@ -2909,13 +2940,14 @@ def check_input_endpoint_exceptions(self): self.send_basic_event(source_iwidget) self.check_wiring_badge("1") - with self.wiring_view as wiring: - with wiring.component_sidebar as sidebar: - operators = sidebar.find_components('operator', 'Wirecloud/TestOperator', state='in use') - operator = wiring.find_draggable_component('operator', id=operators[0].id) - modal = operator.show_logs() - self.assertEqual(len(modal.find_alerts(state='error')), 1) - modal.accept() + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: + with wiring.component_sidebar as sidebar: + operators = sidebar.find_components('operator', 'Wirecloud/TestOperator', state='in use') + operator = wiring.find_draggable_component('operator', id=operators[0].id) + modal = operator.show_logs() + self.assertEqual(len(modal.find_alerts(state='error')), 1) + modal.accept() def test_input_endpoint_exceptions(self): user = User.objects.get(pk=4) @@ -2956,11 +2988,12 @@ def test_type_error_and_value_exceptions(self): self.check_wiring_badge("4") - with self.wiring_view as wiring: - connections = wiring.find_connections(extra_class='has-error') - self.assertEqual(len(connections), 2) - self._check_connection_errors(connections[0], 2) - self._check_connection_errors(connections[1], 2) + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: + connections = wiring.find_connections(extra_class='has-error') + self.assertEqual(len(connections), 2) + self._check_connection_errors(connections[0], 2) + self._check_connection_errors(connections[1], 2) def test_input_endpoint_no_handler_exceptions(self): @@ -2985,55 +3018,57 @@ def test_missing_input_and_output_endpoints(self): self.login(username='user_with_workspaces', next='/user_with_workspaces/Workspace') - with self.wiring_view as wiring: - self.assertEqual(len(wiring.find_connections(extra_class='missing')), 2) + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: + self.assertEqual(len(wiring.find_connections(extra_class='missing')), 2) test_missing_input_and_output_endpoints.tags = tags + ('wirecloud-wiring-endpoint-management',) @uses_extra_workspace('user_with_workspaces', 'Wirecloud_test-mashup-multiendpoint_1.0.wgt', shared=True) def test_ordering_component_endpoints(self): self.login(username='user_with_workspaces', next='/user_with_workspaces/test-mashup-multiendpoint') - with self.wiring_view as wiring: - operator = wiring.find_draggable_component('operator', title="TestOp. Multiendpoint") - widget = wiring.find_draggable_component('widget', title="Test_Multiendpoint") + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: + operator = wiring.find_draggable_component('operator', title="TestOp. Multiendpoint") + widget = wiring.find_draggable_component('widget', title="Test_Multiendpoint") - source = widget.find_endpoint('source', 'output1') - target = operator.find_endpoint('target', 'input2') - connection = source.create_connection(target) + source = widget.find_endpoint('source', 'output1') + target = operator.find_endpoint('target', 'input2') + connection = source.create_connection(target) - operator_targets_count = len(operator.find_endpoints('target')) - operator_sources_count = len(operator.find_endpoints('target')) - widget_targets_count = len(widget.find_endpoints('target')) - widget_sources_count = len(widget.find_endpoints('target')) + operator_targets_count = len(operator.find_endpoints('target')) + operator_sources_count = len(operator.find_endpoints('target')) + widget_targets_count = len(widget.find_endpoints('target')) + widget_sources_count = len(widget.find_endpoints('target')) - with operator.order_endpoints as component_editable: - component_editable.move_endpoint('source', 'output1', 'output2') - component_editable.move_endpoint('target', 'input2', 'input3', must_change=(connection,)) + with operator.order_endpoints as component_editable: + component_editable.move_endpoint('source', 'output1', 'output2') + component_editable.move_endpoint('target', 'input2', 'input3', must_change=(connection,)) - with widget.order_endpoints as component_editable: - component_editable.move_endpoint('source', 'output1', 'output2', must_change=(connection,)) - component_editable.move_endpoint('target', 'input1', 'input3') + with widget.order_endpoints as component_editable: + component_editable.move_endpoint('source', 'output1', 'output2', must_change=(connection,)) + component_editable.move_endpoint('target', 'input1', 'input3') - self.assertEqual(operator_targets_count, len(operator.find_endpoints('target'))) - self.assertEqual(operator_sources_count, len(operator.find_endpoints('source'))) - self.assertEqual(widget_targets_count, len(widget.find_endpoints('target'))) - self.assertEqual(widget_sources_count, len(widget.find_endpoints('source'))) + self.assertEqual(operator_targets_count, len(operator.find_endpoints('target'))) + self.assertEqual(operator_sources_count, len(operator.find_endpoints('source'))) + self.assertEqual(widget_targets_count, len(widget.find_endpoints('target'))) + self.assertEqual(widget_sources_count, len(widget.find_endpoints('source'))) - with self.wiring_view as wiring: - operator = wiring.find_draggable_component('operator', id=operator.id) - widget = wiring.find_draggable_component('widget', id=widget.id) + with edit_session.wiring_view as wiring: + operator = wiring.find_draggable_component('operator', id=operator.id) + widget = wiring.find_draggable_component('widget', id=widget.id) - self.assertEqual(operator_targets_count, len(operator.find_endpoints('target'))) - self.assertEqual(operator_sources_count, len(operator.find_endpoints('source'))) - self.assertEqual(widget_targets_count, len(widget.find_endpoints('target'))) - self.assertEqual(widget_sources_count, len(widget.find_endpoints('source'))) + self.assertEqual(operator_targets_count, len(operator.find_endpoints('target'))) + self.assertEqual(operator_sources_count, len(operator.find_endpoints('source'))) + self.assertEqual(widget_targets_count, len(widget.find_endpoints('target'))) + self.assertEqual(widget_sources_count, len(widget.find_endpoints('source'))) - # Ordering endpoints should be disabled for collapsed components - operator.collapse_endpoints() + # Ordering endpoints should be disabled for collapsed components + operator.collapse_endpoints() - menu_dropdown = operator.show_preferences() - menu_dropdown.check(must_be_disabled=("Order endpoints",)) - menu_dropdown.close() + menu_dropdown = operator.show_preferences() + menu_dropdown.check(must_be_disabled=("Order endpoints",)) + menu_dropdown.close() test_ordering_component_endpoints.tags = tags + ('wirecloud-wiring-endpoint-management',) def test_behaviour_engine_basic_features(self): @@ -3042,30 +3077,32 @@ def test_behaviour_engine_basic_features(self): # Enable it self.login(username='user_with_workspaces', next='/user_with_workspaces/WiringTests') - with self.wiring_view as wiring: - with wiring.behaviour_sidebar as sidebar: - sidebar.btn_enable.click() - self.assertFalse(sidebar.disabled) - # Check that there is an initial behaviour - self.assertEqual(len(sidebar.find_behaviours()), 1) - sidebar.active_behaviour.check_info("New behaviour", "No description provided.") + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: + with wiring.behaviour_sidebar as sidebar: + sidebar.btn_enable.click() + self.assertFalse(sidebar.disabled) + # Check that there is an initial behaviour + self.assertEqual(len(sidebar.find_behaviours()), 1) + sidebar.active_behaviour.check_info("New behaviour", "No description provided.") # Check the change is correctly persisted self.reload() - self.wait_wirecloud_ready() - - with self.wiring_view as wiring: - with wiring.behaviour_sidebar as sidebar: - self.assertFalse(sidebar.disabled) - self.assertEqual(len(sidebar.find_behaviours()), 1) - sidebar.active_behaviour.check_info("New behaviour", "No description provided.") - behaviour1 = sidebar.active_behaviour - - # Create a new behaviour - behaviour2 = sidebar.create_behaviour("Title", "Description") - - # Change behaviour order - sidebar.btn_order.click() - behaviour1.change_position(behaviour2) - sidebar.btn_order.click() + self.wait_wirecloud_ready(login=True) + + with self.edit_mode as edit_session: + with edit_session.wiring_view as wiring: + with wiring.behaviour_sidebar as sidebar: + self.assertFalse(sidebar.disabled) + self.assertEqual(len(sidebar.find_behaviours()), 1) + sidebar.active_behaviour.check_info("New behaviour", "No description provided.") + behaviour1 = sidebar.active_behaviour + + # Create a new behaviour + behaviour2 = sidebar.create_behaviour("Title", "Description") + + # Change behaviour order + sidebar.btn_order.click() + behaviour1.change_position(behaviour2) + sidebar.btn_order.click() test_behaviour_engine_basic_features.tags = tags + ('wirecloud-wiring-behaviour-management',) From e1d4f27b805c99c7a2bcef0af47171df1d8e592d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Arranz?= Date: Fri, 22 Mar 2019 18:51:32 +0100 Subject: [PATCH 15/22] Provide an active event on Toggle buttons --- .../commons/static/js/StyledElements/ToggleButton.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/wirecloud/commons/static/js/StyledElements/ToggleButton.js b/src/wirecloud/commons/static/js/StyledElements/ToggleButton.js index 65620701d0..d9d8e644f8 100644 --- a/src/wirecloud/commons/static/js/StyledElements/ToggleButton.js +++ b/src/wirecloud/commons/static/js/StyledElements/ToggleButton.js @@ -33,13 +33,20 @@ options = utils.merge(defaultOptions, options); StyledElements.Button.call(this, options); + this.events.active = new StyledElements.Event(this); Object.defineProperty(this, 'active', { get: function get() { return this.hasClassName('active'); }, set: function set(value) { - this.toggleClassName('active', value); + // Convert value to boolean, just in case + value = !!value; + let current = this.hasClassName('active'); + if (current !== value) { + this.toggleClassName('active', value); + this.dispatchEvent('active', value); + } } }); From 344919e1f5cca6ca82c09f0006086c3d24a24eb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Arranz?= Date: Fri, 22 Mar 2019 18:52:54 +0100 Subject: [PATCH 16/22] Initial work on displaying widget menus for volatile widgets and controlling the available buttons on it --- .../defaulttheme/static/css/_defaults.scss | 2 +- .../static/css/workspace/widget.scss | 55 ++++--------------- .../templates/wirecloud/workspace/widget.html | 2 +- src/wirecloud/guidebuilder/tests.py | 3 +- .../static/js/wirecloud/ui/WidgetView.js | 21 +++++-- .../static/js/wirecloud/ui/WorkspaceView.js | 5 ++ 6 files changed, 35 insertions(+), 53 deletions(-) diff --git a/src/wirecloud/defaulttheme/static/css/_defaults.scss b/src/wirecloud/defaulttheme/static/css/_defaults.scss index b359c47654..52155c3e2b 100644 --- a/src/wirecloud/defaulttheme/static/css/_defaults.scss +++ b/src/wirecloud/defaulttheme/static/css/_defaults.scss @@ -70,7 +70,7 @@ $font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace; $headings-font-family: inherit !default; $headings-font-weight: bold !default; -$headings-line-height: 1.1 !default; +$headings-line-height: 1.2 !default; $headings-color: inherit !default; $font-size-h1: floor($font-size-base * 2.75) !default; // 38px $font-size-h2: floor($font-size-base * 2.25) !default; // 32px diff --git a/src/wirecloud/defaulttheme/static/css/workspace/widget.scss b/src/wirecloud/defaulttheme/static/css/workspace/widget.scss index 694e635f5c..48ee4da0b1 100644 --- a/src/wirecloud/defaulttheme/static/css/workspace/widget.scss +++ b/src/wirecloud/defaulttheme/static/css/workspace/widget.scss @@ -34,9 +34,15 @@ position: relative; flex-grow: 0; display: none; + width: 100%; - .wc-workspace-editing & { - display: block; + .wc-workspace-editing &, + .wc-floating-widget > & { + display: flex; + } + + & > h4 { + flex-grow: 1; } & > span { @@ -44,45 +50,15 @@ } & .se-btn { - font-size: 75%; color: rgb(136, 136, 136); transition: color 1s ease-in; } -} -.wc-widget-buttons { - display: block; - position: absolute; - top: $panel-padding-vertical; - right: 0px; - background-image: linear-gradient(to right, rgba($panel-default-heading-bg, 0), $panel-default-heading-bg 38px, $panel-default-heading-bg); - padding: 0px $panel-padding-horizontal 0px 38px; - opacity: 0; - white-space: nowrap; - transition: opacity 1s ease-in; - font-size: $font-size-h4; - line-height: $headings-line-height; - - .wc-widget-heading:hover > & { - opacity: 1; + & .se-btn.disabled { + display: none; } } -.wc-widget-infobuttons { - padding: 0px 38px 0px $panel-padding-horizontal; - background-image: linear-gradient(to left, rgba($panel-default-heading-bg, 0), $panel-default-heading-bg 38px, $panel-default-heading-bg); - position: absolute; - top: $panel-padding-vertical; - left: 0px; - margin: 0px; - font-size: $font-size-h4; - line-height: $headings-line-height; -} - -.wc-widget-heading .errorbutton.disabled { - display: none; -} - .wc-widget-body { background: $widget-bg; flex-grow: 1; @@ -111,7 +87,8 @@ background: $widget-footer-bg; } - .wc-workspace-editing & { + .wc-workspace-editing &, + .wc-floating-widget > & { display: block; } } @@ -205,14 +182,6 @@ .wc-widget-highlight { @include z-depth(2, $color: $state-success-border); @include animation(highlight_iwidget 5s linear); - - & .wc-widget-buttons { - background-image: linear-gradient(to right, rgba($panel-success-heading-bg, 0), $panel-success-heading-bg 38px, $panel-success-heading-bg); - } - - & .wc-widget-infobuttons{ - background-image: linear-gradient(to left, rgba($panel-success-heading-bg, 0), $panel-success-heading-bg 38px, $panel-success-heading-bg); - } } .wc-minimized-widget { diff --git a/src/wirecloud/defaulttheme/templates/wirecloud/workspace/widget.html b/src/wirecloud/defaulttheme/templates/wirecloud/workspace/widget.html index a482bbb735..1200ba35e0 100644 --- a/src/wirecloud/defaulttheme/templates/wirecloud/workspace/widget.html +++ b/src/wirecloud/defaulttheme/templates/wirecloud/workspace/widget.html @@ -1,6 +1,6 @@
-

+

diff --git a/src/wirecloud/guidebuilder/tests.py b/src/wirecloud/guidebuilder/tests.py index c8f8ca9aeb..731af825b1 100644 --- a/src/wirecloud/guidebuilder/tests.py +++ b/src/wirecloud/guidebuilder/tests.py @@ -520,8 +520,7 @@ def take_capture(*args, **kargs): # Widget menu button widg_menu = map_viewer_widget.element.find_element_by_css_selector('.wc-widget-heading') - setts_btn = widg_menu.find_element_by_css_selector( - '.wc-widget-buttons .fa-cogs') + setts_btn = widg_menu.find_element_by_css_selector('.wc-menu-button') ActionChains(self.driver).move_to_element(setts_btn).perform() time.sleep(0.3) # Wait menu button hover effect imgp = take_capture(self.driver, "widget_menu_button") diff --git a/src/wirecloud/platform/static/js/wirecloud/ui/WidgetView.js b/src/wirecloud/platform/static/js/wirecloud/ui/WidgetView.js index ae33b8800b..5dc8f4d6b6 100644 --- a/src/wirecloud/platform/static/js/wirecloud/ui/WidgetView.js +++ b/src/wirecloud/platform/static/js/wirecloud/ui/WidgetView.js @@ -111,7 +111,7 @@ title: utils.gettext("Remove") }); - button.setDisabled(!view.model.isAllowed('close')); + view.closebutton = button; button.addEventListener('click', function () { view.remove(); }); @@ -138,6 +138,7 @@ title: utils.gettext("Menu") }); + view.menubutton = button; button.popup_menu.append(new ns.WidgetViewMenuItems(view)); return button; }, @@ -148,7 +149,7 @@ title: utils.gettext("Minimize") }); - button.setDisabled(!view.model.isAllowed('minimize')); + button.enable = view.model.isAllowed('minimize'); button.addEventListener('click', function (button) { view.toggleMinimizeStatus(true); }); @@ -168,7 +169,6 @@ var handle = new Wirecloud.ui.WidgetViewResizeHandle(view, {resizeLeftSide: true, fixWidth: true}); handle.addClassName("wc-bottom-resize-handle"); - handle.setDisabled(!view.model.isAllowed('resize')); view.bottomresizehandle = handle; return handle; }, @@ -176,7 +176,6 @@ var handle = new Wirecloud.ui.WidgetViewResizeHandle(view, {resizeLeftSide: true}); handle.addClassName("wc-bottom-left-resize-handle"); - handle.setDisabled(!view.model.isAllowed('resize')); view.leftresizehandle = handle; return handle; }, @@ -184,7 +183,6 @@ var handle = new Wirecloud.ui.WidgetViewResizeHandle(view, {resizeLeftSide: false}); handle.addClassName("wc-bottom-right-resize-handle"); - handle.setDisabled(!view.model.isAllowed('resize')); view.rightresizehandle = handle; return handle; }, @@ -259,7 +257,9 @@ this.repaint(); }.bind(this)); + this.tab.workspace.addEventListener('editmode', update_buttons.bind(this)); model.addEventListener('remove', on_remove.bind(this)); + update_buttons.call(this); }; // ========================================================================= @@ -560,6 +560,15 @@ var privates = new WeakMap(); + var update_buttons = function update_buttons() { + this.closebutton.enabled = (this.model.volatile || this.tab.workspace.editing) && this.model.isAllowed('close'); + this.menubutton.enabled = this.tab.workspace.editing; + + this.bottomresizehandle.enabled = (this.model.volatile || this.tab.workspace.editing) && this.model.isAllowed('resize'); + this.leftresizehandle.enabled = (this.model.volatile || this.tab.workspace.editing) && this.model.isAllowed('resize'); + this.rightresizehandle.enabled = (this.model.volatile || this.tab.workspace.editing) && this.model.isAllowed('resize'); + }; + var update_className = function update_className() { if (this.model.missing) { this.wrapperElement.classList.add('wc-missing-widget'); @@ -608,7 +617,7 @@ var on_add_log = function on_add_log() { var label, errorCount = this.model.logManager.errorCount; - this.errorbutton.setDisabled(errorCount === 0); + this.errorbutton.enabled = errorCount !== 0; label = utils.ngettext("%(errorCount)s error", "%(errorCount)s errors", errorCount); label = utils.interpolate(label, {errorCount: errorCount}, true); diff --git a/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceView.js b/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceView.js index 1f66be55be..cac4534e4f 100644 --- a/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceView.js +++ b/src/wirecloud/platform/static/js/wirecloud/ui/WorkspaceView.js @@ -29,6 +29,8 @@ var WorkspaceView = function WorkspaceView(id, options) { StyledElements.Alternative.call(this, id, options); + this.events.editmode = new StyledElements.Event(this); + this.wrapperElement.classList.add("wc-workspace"); this.wsMenu = new StyledElements.PopupMenu(); @@ -50,6 +52,9 @@ } this.activeTab.dragboard._updateIWidgetSizes(true, true); }); + this.editButton.addEventListener("active", (button) => { + this.dispatchEvent("editmode", this.editing); + }); this.walletButton = this.buildAddWidgetButton(); From 5d624ebd87a8de41899e70400dcfe615bb9ab605 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Arranz?= Date: Fri, 5 Apr 2019 12:43:14 +0200 Subject: [PATCH 17/22] Readd support for the context option when using the StyledElements.Notebook.goToTab method --- src/js_tests/styledelements/NotebookSpec.js | 21 +++++++++++++------ .../static/js/StyledElements/Notebook.js | 12 ++++++++--- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/js_tests/styledelements/NotebookSpec.js b/src/js_tests/styledelements/NotebookSpec.js index 3be41dc2d3..dcfe9182ee 100644 --- a/src/js_tests/styledelements/NotebookSpec.js +++ b/src/js_tests/styledelements/NotebookSpec.js @@ -332,19 +332,28 @@ it("should allow to move to a middle tab", function () { element.goToTab(tab2); expect(element.visibleTab).toBe(tab2); - expect(changelistener).toHaveBeenCalledWith(element, tab1, tab2); - expect(changedlistener).toHaveBeenCalledWith(element, tab1, tab2); + expect(changelistener).toHaveBeenCalledWith(element, tab1, tab2, undefined); + expect(changedlistener).toHaveBeenCalledWith(element, tab1, tab2, undefined); expect(element.focus).toHaveBeenCalledWith(tab2.tabId); }); it("should allow to move to the last tab", function () { element.goToTab(tab3); expect(element.visibleTab).toBe(tab3); - expect(changelistener).toHaveBeenCalledWith(element, tab1, tab3); - expect(changedlistener).toHaveBeenCalledWith(element, tab1, tab3); + expect(changelistener).toHaveBeenCalledWith(element, tab1, tab3, undefined); + expect(changedlistener).toHaveBeenCalledWith(element, tab1, tab3, undefined); expect(element.focus).toHaveBeenCalledWith(tab3.tabId); }); + it("should support the context option", () => { + element.goToTab(tab2, {context: "mycontext"}); + + expect(element.visibleTab).toBe(tab2); + expect(changelistener).toHaveBeenCalledWith(element, tab1, tab2, "mycontext"); + expect(changedlistener).toHaveBeenCalledWith(element, tab1, tab2, "mycontext"); + expect(element.focus).toHaveBeenCalledWith(tab2.tabId); + }); + it("does nothing if the passed tab is the visible tab (focusOnSetVisible: false)", () => { element = new StyledElements.Notebook({focusOnSetVisible: false}); element.appendTo(dom); @@ -372,8 +381,8 @@ element.goToTab(tab2); expect(element.visibleTab).toBe(tab2); - expect(changelistener).toHaveBeenCalledWith(element, tab1, tab2); - expect(changedlistener).toHaveBeenCalledWith(element, tab1, tab2); + expect(changelistener).toHaveBeenCalledWith(element, tab1, tab2, undefined); + expect(changedlistener).toHaveBeenCalledWith(element, tab1, tab2, undefined); expect(element.focus).not.toHaveBeenCalled(); }); diff --git a/src/wirecloud/commons/static/js/StyledElements/Notebook.js b/src/wirecloud/commons/static/js/StyledElements/Notebook.js index bfacaeb936..20aa530b4b 100644 --- a/src/wirecloud/commons/static/js/StyledElements/Notebook.js +++ b/src/wirecloud/commons/static/js/StyledElements/Notebook.js @@ -510,8 +510,10 @@ * foco a la pestaƱa asociada. * * @param {Number|Tab} tab intance or tab id of the tab to make visible + * @param {Object.} options + * - context: context data to be sent on the change and the changed events */ - Notebook.prototype.goToTab = function goToTab(tab) { + Notebook.prototype.goToTab = function goToTab(tab, options) { var newTab, oldTab; if (tab instanceof StyledElements.Tab) { @@ -527,6 +529,10 @@ } oldTab = this.visibleTab; + if (options == null) { + options = {}; + } + if (this.visibleTab && newTab === this.visibleTab) { if (this.focusOnSetVisible) { this.focus(newTab.tabId); @@ -534,7 +540,7 @@ return; } - this.dispatchEvent('change', oldTab, newTab); + this.dispatchEvent('change', oldTab, newTab, options.context); // At this point there is always a visibleTab // if (this.visibleTab) { @@ -547,7 +553,7 @@ this.focus(newTab.tabId); } - this.dispatchEvent('changed', oldTab, newTab); + this.dispatchEvent('changed', oldTab, newTab, options.context); }; /** From 728153bbdcd4283cd7d933e34be8ad0948cb3517 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Arranz?= Date: Fri, 5 Apr 2019 12:46:53 +0200 Subject: [PATCH 18/22] Improve ObjectWithEvents spec by disable console logging of exceptions thrown intentionally --- src/js_tests/styledelements/ObjectWithEventsSpec.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/js_tests/styledelements/ObjectWithEventsSpec.js b/src/js_tests/styledelements/ObjectWithEventsSpec.js index aa913a29cb..79e2b77396 100644 --- a/src/js_tests/styledelements/ObjectWithEventsSpec.js +++ b/src/js_tests/styledelements/ObjectWithEventsSpec.js @@ -107,11 +107,10 @@ expect(eventListener3).toHaveBeenCalledWith(eventTarget); }); - it("should call all eventListener list despite whether an event listener thrown an error", function () { + it("should call all eventListener listeners despite whether one of them thrown an error", function () { + spyOn(window.console, "error"); var eventTarget = new StyledElements.ObjectWithEvents(['click']); - var eventListener1 = function (element) { - throw Error(); - }; + var eventListener1 = jasmine.createSpy('eventListener1').and.throwError(); var eventListener2 = jasmine.createSpy('eventListener2'); eventTarget.addEventListener('click', eventListener1); @@ -119,7 +118,9 @@ eventTarget.dispatchEvent('click'); + expect(eventListener1).toHaveBeenCalledWith(eventTarget); expect(eventListener2).toHaveBeenCalledWith(eventTarget); + expect(window.console.error).toHaveBeenCalledWith(jasmine.any(Error)); }); }); From 1123c0dd928ee850f57ee880cad1990ee95b5b4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Arranz?= Date: Fri, 5 Apr 2019 12:47:19 +0200 Subject: [PATCH 19/22] Fix waitTransition tests --- src/js_tests/styledelements/UtilsSpec.js | 20 ++++++++++++++++++- .../commons/static/js/StyledElements/Utils.js | 4 ++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/js_tests/styledelements/UtilsSpec.js b/src/js_tests/styledelements/UtilsSpec.js index 440435bf49..8da5802f41 100644 --- a/src/js_tests/styledelements/UtilsSpec.js +++ b/src/js_tests/styledelements/UtilsSpec.js @@ -483,15 +483,31 @@ }); describe("waitTransition(element)", () => { - var waitTransition; + var waitTransition, toclean = null; beforeAll(() => { waitTransition = StyledElements.Utils.waitTransition; }); + afterEach(() => { + if (toclean) { + toclean.remove(); + toclean = null; + } + }); + it("should immediatelly resolve for display: none elements", (done) => { let element = document.createElement("div"); element.style.display = "none"; + document.body.appendChild(element); + toclean = element; + + waitTransition(element).then(done, fail); + }); + + it("should immediatelly resolve for elements not in DOM", (done) => { + let element = document.createElement("div"); + element.style.display = "none"; waitTransition(element).then(done, fail); }); @@ -499,6 +515,8 @@ it("should wait transitionend events on elements", (done) => { let listener = jasmine.createSpy("listener"); let element = document.createElement("div"); + document.body.appendChild(element); + toclean = element; waitTransition(element).then(listener, fail); diff --git a/src/wirecloud/commons/static/js/StyledElements/Utils.js b/src/wirecloud/commons/static/js/StyledElements/Utils.js index 0bf04991ee..6d4e18f33d 100644 --- a/src/wirecloud/commons/static/js/StyledElements/Utils.js +++ b/src/wirecloud/commons/static/js/StyledElements/Utils.js @@ -946,6 +946,10 @@ if (window.StyledElements == null) { Utils.waitTransition = function waitTransition(element) { return new Promise((fulfill) => { + if (element.parentNode === null) { + fulfill(); + } + let w = element.ownerDocument.defaultView; let display = w.getComputedStyle(element, null).getPropertyValue("display"); if (display !== "none") { From c41f4d9df5edc22b608de378a69bfbd7cc0feb40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Arranz?= Date: Fri, 5 Apr 2019 12:47:50 +0200 Subject: [PATCH 20/22] Fix selenium tests --- .../static/js/wirecloud/Tutorials/BasicConcepts.js | 2 ++ src/wirecloud/platform/tests/selenium.py | 9 ++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/wirecloud/platform/static/js/wirecloud/Tutorials/BasicConcepts.js b/src/wirecloud/platform/static/js/wirecloud/Tutorials/BasicConcepts.js index 41a8ce406d..351fecc7ab 100644 --- a/src/wirecloud/platform/static/js/wirecloud/Tutorials/BasicConcepts.js +++ b/src/wirecloud/platform/static/js/wirecloud/Tutorials/BasicConcepts.js @@ -74,6 +74,8 @@ {'type': 'autoAction', 'action': BA.uploadComponent('CoNWeT/input-box/1.0')}, {'type': 'autoAction', 'action': BA.uploadComponent('CoNWeT/youtube-browser/3.0')}, {'type': 'simpleDescription', 'title': utils.gettext('WireCloud Basic Tutorial'), 'msg': utils.gettext("

Ok, widgets have been installed successfuly.

Next step is to add the YouTube Browser widget to the workspace.

")}, + {'type': 'userAction', 'msg': utils.gettext("Click the Edit button"), 'elem': BS.toolbar_button('wc-edit-mode-button'), 'pos': 'downLeft'}, + {'type': 'autoAction', 'elem': BS.toolbar_button('wc-edit-mode-button'), 'action': BA.sleep(500)}, {'type': 'userAction', 'msg': utils.gettext("Click the Add components button"), 'elem': BS.toolbar_button('wc-show-component-sidebar-button'), 'pos': 'downLeft'}, {'type': 'autoAction', 'elem': BS.toolbar_button('wc-show-component-sidebar-button'), 'action': BA.sleep(500)}, {'type': 'autoAction', 'msg': utils.gettext('By typing "browser" we can filter widgets that contains in their name or description these words'), 'elem': BS.mac_wallet_input(), 'pos': 'downRight', 'action': BA.input('browser', {send: true})}, diff --git a/src/wirecloud/platform/tests/selenium.py b/src/wirecloud/platform/tests/selenium.py index 46fea74cf7..55ddc1ac59 100644 --- a/src/wirecloud/platform/tests/selenium.py +++ b/src/wirecloud/platform/tests/selenium.py @@ -340,6 +340,7 @@ def test_widget_navigation_to_doc(self): self.login(username='user_with_workspaces', next="/user_with_workspaces/Workspace") iwidget = self.widgets[0] + self.edit_mode.__enter__() iwidget.open_menu().click_entry("User's Manual") WebDriverWait(self.driver, timeout=5).until(lambda driver: self.get_current_view() == 'myresources') @@ -635,8 +636,6 @@ def test_workspace_publish_readonly_widgets_and_connections(self): iwidget = self.widgets[0] close_button = ButtonTester(self, iwidget.element.find_element_by_css_selector('.fa-remove')) self.assertTrue(close_button.is_disabled) - # bypass the internal call to element_be_clickable as the button is usually hidden - close_button.element.click() with edit_session.wiring_view as wiring: self.assertEqual(len(wiring.find_connections(extra_class="readonly")), 3) @@ -946,8 +945,8 @@ def test_gui_tutorials(self): self.assertElementHasFocus(next_button) next_button.click() - WebDriverWait(self.driver, 10).until(WEC.element_be_clickable((By.CSS_SELECTOR, ".wc-toolbar .wc-show-component-sidebar-button"))) with self.edit_mode as edit_session: + WebDriverWait(self.driver, 10).until(WEC.element_be_clickable((By.CSS_SELECTOR, ".wc-toolbar .wc-show-component-sidebar-button"))) with edit_session.resource_sidebar as sidebar: # Add the youtube browser widget @@ -961,8 +960,8 @@ def test_gui_tutorials(self): # Add the input box widget WebDriverWait(self.driver, timeout=15).until(WEC.component_instantiable(sidebar, 'Input Box')) - # cancel current tutorial - self.wait_element_visible_by_xpath("//*[contains(@class, 'window_menu')]//*[text()='Cancel']").click() + # cancel current tutorial + self.wait_element_visible_by_xpath("//*[contains(@class, 'window_menu')]//*[text()='Cancel']").click() window_menues = self.driver.find_elements_by_css_selector('.window_menu') self.assertEqual(len(window_menues), 1) From 4d65bb6f3b4fa6c7f0529f91b28d3b8f6eda68e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Arranz?= Date: Tue, 16 Apr 2019 09:48:14 +0200 Subject: [PATCH 21/22] Remove wiring-view CSS class --- src/wirecloud/commons/utils/remote.py | 2 +- .../defaulttheme/static/css/wiring/components.scss | 2 +- src/wirecloud/guidebuilder/tests.py | 6 +++--- .../platform/static/js/wirecloud/ui/WiringEditor.js | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/wirecloud/commons/utils/remote.py b/src/wirecloud/commons/utils/remote.py index 0d28221731..39462b3928 100644 --- a/src/wirecloud/commons/utils/remote.py +++ b/src/wirecloud/commons/utils/remote.py @@ -2090,7 +2090,7 @@ def component_sidebar(self): @property def disabled(self): - return 'disabled' in self.testcase.driver.find_element_by_css_selector(".wiring-view").get_attribute('class').split() + return 'disabled' in self.testcase.driver.find_element_by_css_selector(".wc-workspace-wiring").get_attribute('class').split() def select(self, components=(), key=Keys.CONTROL): actions = ActionChains(self.testcase.driver) diff --git a/src/wirecloud/defaulttheme/static/css/wiring/components.scss b/src/wirecloud/defaulttheme/static/css/wiring/components.scss index 91d775e4f5..12ad790bcc 100644 --- a/src/wirecloud/defaulttheme/static/css/wiring/components.scss +++ b/src/wirecloud/defaulttheme/static/css/wiring/components.scss @@ -538,7 +538,7 @@ // WIRING VIEW - COMPONENT - COLLAPSED // ============================================================================ -.wiring-view .component-draggable.collapsed { +.wc-workspace-wiring .component-draggable.collapsed { .endpoints { position: absolute; diff --git a/src/wirecloud/guidebuilder/tests.py b/src/wirecloud/guidebuilder/tests.py index 731af825b1..377f40f60e 100644 --- a/src/wirecloud/guidebuilder/tests.py +++ b/src/wirecloud/guidebuilder/tests.py @@ -589,11 +589,11 @@ def take_capture(*args, **kargs): with self.wiring_view as wiring: ActionChains(self.driver).move_by_offset(0, 50).perform() - self.wait_element_visible('.wiring-view .se-alert-static-top') + self.wait_element_visible('.wc-workspace-wiring .se-alert-static-top') time.sleep(0.2) imgp = take_capture(self.driver, 'empty_wiring') crop_down( - imgp, self.driver.find_element_by_css_selector(".wiring-view .se-alert-static-top"), 40) + imgp, self.driver.find_element_by_css_selector(".wc-workspace-view .se-alert-static-top"), 40) # Click in Find Components btn = self.find_navbar_button('we-show-component-sidebar-button') @@ -966,7 +966,7 @@ def take_capture(*args, **kargs): add_pointer(imgp, get_position(btnbehav, 0.8, 0.5)) crop_down(imgp, btnbehav, 30) - self.driver.execute_script("document.querySelector('.wiring-view .wiring-diagram').style.cssText = 'box-shadow: none; border: none;'") + self.driver.execute_script("document.querySelector('.wc-workspace-view .wiring-diagram').style.cssText = 'box-shadow: none; border: none;'") wc = self.driver.find_element_by_css_selector(".we-connections-layer") with wiring.behaviour_sidebar as sidebar: diff --git a/src/wirecloud/platform/static/js/wirecloud/ui/WiringEditor.js b/src/wirecloud/platform/static/js/wirecloud/ui/WiringEditor.js index 694161ebe6..7b2983dfae 100644 --- a/src/wirecloud/platform/static/js/wirecloud/ui/WiringEditor.js +++ b/src/wirecloud/platform/static/js/wirecloud/ui/WiringEditor.js @@ -51,7 +51,7 @@ Wirecloud.ui = Wirecloud.ui || {}; */ ns.WiringEditor = function WiringEditor(id, options) { options = utils.merge({}, options); - options.class = "wiring-view wc-workspace-wiring"; + options.class = "wc-workspace-wiring"; se.Alternative.call(this, id, options); From 1e63d1fa79d8772f552d6e4cf0d92858ceda6466 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Arranz?= Date: Tue, 16 Apr 2019 10:48:07 +0200 Subject: [PATCH 22/22] Fixes on the selenium tests --- src/wirecloud/commons/utils/remote.py | 4 ++-- src/wirecloud/platform/wiring/tests.py | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/wirecloud/commons/utils/remote.py b/src/wirecloud/commons/utils/remote.py index 39462b3928..f2298a94c4 100644 --- a/src/wirecloud/commons/utils/remote.py +++ b/src/wirecloud/commons/utils/remote.py @@ -1146,7 +1146,7 @@ def change_position(self, endpoint): def create_connection(self, endpoint, must_recommend=(), must_expand=()): ActionChains(self.testcase.driver).click_and_hold(self.element).perform() # Wait until the browser reacts - time.sleep(0.2) + time.sleep(0.4) WebDriverWait(self.testcase.driver, 5).until(lambda driver: self.is_active) for endpoint in must_recommend: self.testcase.assertTrue(endpoint.is_active) @@ -1154,7 +1154,7 @@ def create_connection(self, endpoint, must_recommend=(), must_expand=()): self.testcase.assertFalse(component.has_class('collapsed')) ActionChains(self.testcase.driver).move_to_element(endpoint.element).release().perform() - return self.find_connection(endpoint) + return WebDriverWait(self.testcase.driver, 5).until(lambda driver: self.find_connection(endpoint)) def find_connection(self, endpoint): for connection in self.find_connections(): diff --git a/src/wirecloud/platform/wiring/tests.py b/src/wirecloud/platform/wiring/tests.py index f2ebf0dfd0..73dcee6f2c 100644 --- a/src/wirecloud/platform/wiring/tests.py +++ b/src/wirecloud/platform/wiring/tests.py @@ -2755,6 +2755,9 @@ def test_remove_components_using_key_delete_when_behaviour_engine_is_enabled(sel self.assertNotIn('TestOperator', modal.body.text) modal.accept() + # Wait until the browser reacts + time.sleep(0.4) + self.assertIsNone(wiring.find_draggable_component('widget', title="Test 2")) self.assertTrue(wiring.find_draggable_component('operator', id=1).has_class('background')) self.assertTrue(wiring.find_draggable_component('widget', title="Test 1").has_class('background'))