// Copyright 2010 Google Inc. All Rights Reseved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.testing.testify.risk.frontend.client; import com.google.common.base.Function; import com.google.common.collect.Lists; import com.google.gwt.core.client.GWT; import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.shared.EventBus; import com.google.gwt.event.shared.SimpleEventBus; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiFactory; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.uibinder.client.UiHandler; import com.google.gwt.user.client.History; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.ListBox; import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.Widget; import com.google.testing.testify.risk.frontend.client.event.ProjectChangedEvent; import com.google.testing.testify.risk.frontend.client.event.ProjectChangedHandler; import com.google.testing.testify.risk.frontend.client.event.ProjectElementAddedEvent; import com.google.testing.testify.risk.frontend.client.event.ProjectElementAddedHandler; import com.google.testing.testify.risk.frontend.client.event.ProjectHasNoElementsEvent; import com.google.testing.testify.risk.frontend.client.event.ProjectHasNoElementsHandler; import com.google.testing.testify.risk.frontend.client.presenter.AttributesPresenter; import com.google.testing.testify.risk.frontend.client.presenter.BasePagePresenter; import com.google.testing.testify.risk.frontend.client.presenter.CapabilitiesPresenter; import com.google.testing.testify.risk.frontend.client.presenter.CapabilityDetailsPresenter; import com.google.testing.testify.risk.frontend.client.presenter.ComponentsPresenter; import com.google.testing.testify.risk.frontend.client.presenter.ConfigureDataPresenter; import com.google.testing.testify.risk.frontend.client.presenter.ConfigureFiltersPresenter; import com.google.testing.testify.risk.frontend.client.presenter.KnownRiskPresenter; import com.google.testing.testify.risk.frontend.client.presenter.ProjectSettingsPresenter; import com.google.testing.testify.risk.frontend.client.presenter.TaPagePresenter; import com.google.testing.testify.risk.frontend.client.util.NotificationUtil; import com.google.testing.testify.risk.frontend.client.view.impl.AttributesViewImpl; import com.google.testing.testify.risk.frontend.client.view.impl.CapabilitiesViewImpl; import com.google.testing.testify.risk.frontend.client.view.impl.CapabilityDetailsViewImpl; import com.google.testing.testify.risk.frontend.client.view.impl.ComponentsViewImpl; import com.google.testing.testify.risk.frontend.client.view.impl.ConfigureDataViewImpl; import com.google.testing.testify.risk.frontend.client.view.impl.ConfigureFiltersViewImpl; import com.google.testing.testify.risk.frontend.client.view.impl.KnownRiskViewImpl; import com.google.testing.testify.risk.frontend.client.view.impl.ProjectDataViewImpl; import com.google.testing.testify.risk.frontend.client.view.impl.ProjectSettingsViewImpl; import com.google.testing.testify.risk.frontend.client.view.widgets.NavigationLink; import com.google.testing.testify.risk.frontend.client.view.widgets.ProjectFavoriteStar; import com.google.testing.testify.risk.frontend.client.view.widgets.PageHeaderWidget; import com.google.testing.testify.risk.frontend.model.Bug; import com.google.testing.testify.risk.frontend.model.Checkin; import com.google.testing.testify.risk.frontend.model.UploadedDatum; import com.google.testing.testify.risk.frontend.model.Project; import com.google.testing.testify.risk.frontend.model.TestCase; import com.google.testing.testify.risk.frontend.shared.rpc.DataRpcAsync; import com.google.testing.testify.risk.frontend.shared.rpc.ProjectRpcAsync; import com.google.testing.testify.risk.frontend.shared.rpc.UserRpcAsync; import java.util.List; /** * The main application for Test Analytics. Contains the general view area which is populated * with pages that show data. * * @author jimr@google.com (Jim Reardon) */ public class TaApplication extends Composite { protected interface TestifyApplicationUiBinder extends UiBinder<Widget, TaApplication> {} private static final TestifyApplicationUiBinder uiBinder = GWT.create(TestifyApplicationUiBinder.class); @UiField protected SimplePanel contentPanel; @UiField protected ListBox userProjectsListBox; @UiField protected ProjectFavoriteStar projectFavoriteStar; @UiField protected Label projectNameLabel; @UiField protected NavigationLink projectDetailsLink; @UiField protected NavigationLink attributesLink; @UiField protected NavigationLink componentsLink; @UiField protected NavigationLink capabilitiesLink; @UiField protected NavigationLink configureDataLink; @UiField protected NavigationLink configureFiltersLink; @UiField protected NavigationLink projectBugsLink; @UiField protected NavigationLink projectCheckinsLink; @UiField protected NavigationLink projectTestcasesLink; @UiField protected NavigationLink knownRisksLink; // This link is treated differently because it does not show up in the sidebar. private NavigationLink capabilityDetailsLink = new NavigationLink("", -1, "capability-details", null); public static final String PAGE_HISTORY_TOKEN_CAPABILITY_DETAILS = "capability-details"; private final List<NavigationLink> allLinks; private Project project; private final ProjectRpcAsync projectService; private final UserRpcAsync userService; private final DataRpcAsync dataService; /** EventBus for firing and subscribing to application-level events. */ private final EventBus eventBus = new SimpleEventBus(); /** Current page the application is on. */ private NavigationLink currentLink; public TaApplication(Project project, ProjectRpcAsync projectService, UserRpcAsync userService, DataRpcAsync dataService) { this.projectService = projectService; this.userService = userService; this.dataService = dataService; initWidget(uiBinder.createAndBindUi(this)); allLinks = Lists.newArrayList( projectDetailsLink, attributesLink, componentsLink, capabilitiesLink, configureDataLink, configureFiltersLink, projectBugsLink, projectCheckinsLink, projectTestcasesLink, knownRisksLink, capabilityDetailsLink); setProject(project); initializeToolbar(); initializeMenuItems(); hookupEventListeners(); // Default page. switchToPage(projectDetailsLink, ""); } @UiFactory public PageHeaderWidget createTestifyPageHeaderWidget() { return new PageHeaderWidget(userService); } @UiHandler("userProjectsListBox") protected void onSelectNewProject(ChangeEvent event) { int selectedIndex = userProjectsListBox.getSelectedIndex(); History.newItem( "/" + userProjectsListBox.getValue(selectedIndex) + "/" + currentLink.getHistoryTokenName()); } /** * Initialize toolbar widgets; the project list box (which contains projects the user has * explicit access to or are starred) and the star for the current project. */ private void initializeToolbar() { final long currentProjectId = project.getProjectId(); // Add the current project as a place holder while we load other projects the user cares about. userProjectsListBox.addItem(project.getName()); projectService.queryUserProjects( new TaCallback<List<Project>>("querying projects") { @Override public void onSuccess(List<Project> result) { userProjectsListBox.clear(); // The first item in the list box is always the 'current' project. userProjectsListBox.addItem(project.getName(), project.getProjectId().toString()); for (Project p : result) { if (!p.getProjectId().equals(currentProjectId)) { userProjectsListBox.addItem(p.getName(), p.getProjectId().toString()); } } userProjectsListBox.setSelectedIndex(0); } }); // Initialize the project favorite star. projectFavoriteStar.attachToProject(project.getProjectId()); userService.getStarredProjects( new TaCallback<List<Long>>("retrieving starred projects") { @Override public void onSuccess(List<Long> result) { if (result.contains(project.getProjectId())) { projectFavoriteStar.setStarredStatus(true); } } }); // Initialize the project name. projectNameLabel.setText(project.getName()); } private void setProject(Project project) { this.project = project; } public Project getProject() { return project; } public void switchToPage(String historyToken, String pageData) { // If there's no page specified, switch to our default project details page. if (historyToken == null || "".equals(historyToken)) { switchToPage(projectDetailsLink, ""); return; } for (NavigationLink link : allLinks) { if (link.getHistoryTokenName().equals(historyToken)) { switchToPage(link, pageData); return; } } NotificationUtil.displayErrorMessage("The requested page is currently unavailable."); } /** * Switches the current view to the provided Testify application page. */ public void switchToPage(NavigationLink link, String pageData) { NavigationLink oldLink = currentLink; this.currentLink = link; if (oldLink != null) { oldLink.unSelect(); } link.select(); TaPagePresenter presenter = link.getPresenter(); if (presenter != null) { presenter.refreshView(pageData); setAsMainContent(presenter.getView()); } else { // No presenter means something went awry. NotificationUtil.displayErrorMessage("The requested page is currently unavailable."); } } /** Initializes machinery related to navigation via menu items. */ private void initializeMenuItems() { projectDetailsLink.setCreatePresenterFunction(new Function<Void, TaPagePresenter>() { @Override public TaPagePresenter apply(Void input) { return createProjectSettingsPage(); } }); attributesLink.setCreatePresenterFunction(new Function<Void, TaPagePresenter>() { @Override public TaPagePresenter apply(Void input) { return createAttributesPage(); } }); componentsLink.setCreatePresenterFunction(new Function<Void, TaPagePresenter>() { @Override public TaPagePresenter apply(Void input) { return createComponentsPage(); } }); capabilitiesLink.setCreatePresenterFunction(new Function<Void, TaPagePresenter>() { @Override public TaPagePresenter apply(Void input) { return createEditCapabilitiesPage(); } }); configureDataLink.setCreatePresenterFunction(new Function<Void, TaPagePresenter>() { @Override public TaPagePresenter apply(Void input) { return createConfigureDataPage(); } }); configureFiltersLink.setCreatePresenterFunction(new Function<Void, TaPagePresenter>() { @Override public TaPagePresenter apply(Void input) { return createFiltersPage(); } }); projectBugsLink.setCreatePresenterFunction(new Function<Void, TaPagePresenter>() { @Override public TaPagePresenter apply(Void input) { return createBugsPage(); } }); projectCheckinsLink.setCreatePresenterFunction(new Function<Void, TaPagePresenter>() { @Override public TaPagePresenter apply(Void input) { return createCheckinsPage(); } }); projectTestcasesLink.setCreatePresenterFunction(new Function<Void, TaPagePresenter>() { @Override public TaPagePresenter apply(Void input) { return createTestcasesPage(); } }); knownRisksLink.setCreatePresenterFunction(new Function<Void, TaPagePresenter>() { @Override public TaPagePresenter apply(Void input) { return createKnownRiskPage(); } }); capabilityDetailsLink.setCreatePresenterFunction(new Function<Void, TaPagePresenter>() { @Override public TaPagePresenter apply(Void input) { return createCapabilityDetailsPage(); } }); // Update menu item hyper links to include the project number. for (NavigationLink link : allLinks) { link.setProjectId(project.getProjectId()); } } /** * Subscribes top-level UI elements to application-level events. */ private void hookupEventListeners() { /** * Associate all navigation links with project stage. We can then enable or disable links based * on changes the user makes relative to their current project stage. */ final List<NavigationLink> needFullAccModel = Lists.newArrayList( configureDataLink, configureFiltersLink, projectTestcasesLink, projectBugsLink, projectCheckinsLink, knownRisksLink); // If the project has no elements associated with stage n, disable all links to stages after n. eventBus.addHandler(ProjectHasNoElementsEvent.getType(), new ProjectHasNoElementsHandler() { @Override public void onProjectHasNoElements(ProjectHasNoElementsEvent event) { // Disable capabilities link if we don't have both attributes and components. if (event.projectHasNoAttributes() || event.projectHasNoComponents()) { capabilitiesLink.disable(); } // If we're missing any ACC components, disable anything that requires a full model. if (event.projectHasNoAttributes() || event.projectHasNoCapabilities() || event.projectHasNoComponents()) { for (NavigationLink link : needFullAccModel) { link.disable(); } } } }); // If the project adds a new element, enable links to the next stage. eventBus.addHandler(ProjectElementAddedEvent.getType(), new ProjectElementAddedHandler() { @Override public void onProjectElementAdded(ProjectElementAddedEvent event) { if (event.isAttributeAddedEvent() || event.isComponentAddedEvent()) { capabilitiesLink.enable(); } else if (event.isCapabilityAddedEvent()) { for (NavigationLink link : needFullAccModel) { link.enable(); } } } }); } /** * Displays the widget on the main content panel. */ private void setAsMainContent(Widget widget) { if (widget != null) { contentPanel.clear(); contentPanel.setWidget(widget); } } /** * Initializes the Presenter and View for Project Settings. */ private TaPagePresenter createProjectSettingsPage() { ProjectSettingsViewImpl projectSettingsView = new ProjectSettingsViewImpl(); ProjectSettingsPresenter projectSettingsPresenter = new ProjectSettingsPresenter(project, projectService, userService, projectSettingsView, eventBus); // Hook into the Project Settings Presenter's ProjectChanged event. eventBus.addHandler(ProjectChangedEvent.getType(), new ProjectChangedHandler() { @Override public void onProjectChanged(ProjectChangedEvent event) { setProject(event.getProject()); initializeToolbar(); } }); return projectSettingsPresenter; } /** * Initializes the Presenter and View for Attributes page. */ private AttributesPresenter createAttributesPage() { AttributesViewImpl attributesView = new AttributesViewImpl(); AttributesPresenter attributesPresenter = new AttributesPresenter( project, projectService, userService, dataService, attributesView, eventBus); return attributesPresenter; } /** * Initializes the Presenter and View for the Components page. */ private ComponentsPresenter createComponentsPage() { ComponentsViewImpl componentsView = new ComponentsViewImpl(); ComponentsPresenter componentsPresenter = new ComponentsPresenter( project, projectService, userService, dataService, componentsView, eventBus); return componentsPresenter; } /** * Creates the tab which controls a Projects' Capabilities. */ private CapabilitiesPresenter createEditCapabilitiesPage() { CapabilitiesViewImpl capabilitiesView = new CapabilitiesViewImpl(); CapabilitiesPresenter capabilitiesPresenter = new CapabilitiesPresenter( project, projectService, userService, capabilitiesView, eventBus); return capabilitiesPresenter; } private CapabilityDetailsPresenter createCapabilityDetailsPage() { CapabilityDetailsViewImpl detailsView = new CapabilityDetailsViewImpl(); CapabilityDetailsPresenter capabilityDetailsPresenter = new CapabilityDetailsPresenter(project, projectService, dataService, userService, detailsView); return capabilityDetailsPresenter; } /** * Initializes the Presenter and View for the Configure Data page. */ private ConfigureDataPresenter createConfigureDataPage() { ConfigureDataViewImpl view = new ConfigureDataViewImpl(); ConfigureDataPresenter configureDataPresenter = new ConfigureDataPresenter(project, dataService, view); return configureDataPresenter; } private ConfigureFiltersPresenter createFiltersPage() { ConfigureFiltersViewImpl view = new ConfigureFiltersViewImpl(); ConfigureFiltersPresenter filtersPresenter = new ConfigureFiltersPresenter(project, dataService, projectService, view); return filtersPresenter; } /** * Returns a generic page presenter displaying the given view and performing the given * action when refreshView is called. */ private TaPagePresenter createPagePresenter( final Widget mainView, final Function<Void, Void> onRefreshView) { final Label emptyLabel = new Label(""); return new BasePagePresenter() { @Override public Widget getView() { return mainView; } @Override public void refreshView() { onRefreshView.apply(null); } }; } /** Initializes the Presenter and View for the Bugs page. */ private TaPagePresenter createBugsPage() { final Label emptyLabel = new Label(); final ProjectDataViewImpl dataView = new ProjectDataViewImpl(); dataView.setPageText( "Project Bugs", "The following bugs have been uploaded to your Test Analytics project."); Function<Void, Void> onRefreshPage = new Function<Void, Void>() { @Override public Void apply(Void input) { dataService.getProjectBugsById(getProject().getProjectId(), new TaCallback<List<Bug>>("querying project bugs") { @Override public void onSuccess(List<Bug> results) { List<UploadedDatum> converted = Lists.newArrayList(); for (Bug item : results) { converted.add(item); } dataView.displayData(converted); } }); return null; } }; // Start an initial request for project bugs. onRefreshPage.apply(null); TaPagePresenter projectBugsPresenter = createPagePresenter(dataView, onRefreshPage); return projectBugsPresenter; } /** Initializes the Presenter and View for the Checkins page. */ private TaPagePresenter createCheckinsPage() { final Label emptyLabel = new Label(); final ProjectDataViewImpl dataView = new ProjectDataViewImpl(); dataView.setPageText( "Project Checkins", "The following checkins have been uploaded to your Test Analytics project."); Function<Void, Void> onRefreshPage = new Function<Void, Void>() { @Override public Void apply(Void input) { dataService.getProjectCheckinsById(getProject().getProjectId(), new TaCallback<List<Checkin>>("querying project checkins") { @Override public void onSuccess(List<Checkin> results) { List<UploadedDatum> converted = Lists.newArrayList(); for (Checkin item : results) { converted.add(item); } dataView.displayData(converted); } }); return null; } }; // Start an initial request for project checkins. onRefreshPage.apply(null); TaPagePresenter projectCheckinsPresenter = createPagePresenter(dataView, onRefreshPage); return projectCheckinsPresenter; } /** Initializes the Presenter and View for the testcases page. */ private TaPagePresenter createTestcasesPage() { final Label emptyLabel = new Label(); final ProjectDataViewImpl dataView = new ProjectDataViewImpl(); dataView.setPageText( "Project Testcases", "The following testcases have been uploaded to your Test Analytics project."); Function<Void, Void> onRefreshPage = new Function<Void, Void>() { @Override public Void apply(Void input) { dataService.getProjectTestCasesById(getProject().getProjectId(), new TaCallback<List<TestCase>>("querying project testcases") { @Override public void onSuccess(List<TestCase> results) { List<UploadedDatum> converted = Lists.newArrayList(); for (TestCase item : results) { converted.add(item); } dataView.displayData(converted); } }); return null; } }; // Start an initial request for project testcases. onRefreshPage.apply(null); TaPagePresenter projectTestcasesPresenter = createPagePresenter(dataView, onRefreshPage); return projectTestcasesPresenter; } /** * Initialize the Presenter and View for the Known Risks page. */ private KnownRiskPresenter createKnownRiskPage() { KnownRiskViewImpl riskView = new KnownRiskViewImpl(); KnownRiskPresenter knownRiskPresenter = new KnownRiskPresenter(project, projectService, riskView); return knownRiskPresenter; } }