Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add search for tags #416

Merged
merged 19 commits into from
Jan 7, 2020
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [Unreleased]

<!-- ### Added -->
### Added

- Search via URL query [#353](https://github.com/openkfw/TruBudget/issues/353)

### Changed

- The analytics total budget is shown whether the user has insufficient permissions or not [#410](https://github.com/openkfw/TruBudget/pull/410)
- Highlight matches when searching [#356](https://github.com/openkfw/TruBudget/issues/356)
mathiashoeld marked this conversation as resolved.
Show resolved Hide resolved
- Projects can be searched via prefixes. Tag,display name and status are searched for matches. [#359](https://github.com/openkfw/TruBudget/issues/359)

<!-- ### Deprecated -->

Expand Down
16 changes: 15 additions & 1 deletion doc/wiki/User-Guide/Projects/Project.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,20 @@ View all projects where the current user has view-permissions on.

**Description:**

Filter projects based on name, description and tag in the overview page.
There are 3 ways how projects can be filtered:
mathiashoeld marked this conversation as resolved.
Show resolved Hide resolved

1. Searchbar
2. Tag-Button
3. URL

The Searchbar can be used to search all projects for a term included in name, tag or status. Prefixes can be used to specify the search context (e.g. tag:mycustomtag). If no prefix is used display name, tag and status is searched for a match. After typing the project list and the URL are instantly updated. The URL can then be shared to other users including the filter.
The Tag-Button can be clicked to only show projects including the clicked tag.
The URL supports query parameters which are instantly updated when typing search terms into the searchbar.

**Notes:**

- Use prefixes to search specific attributes. Available Prefixes: tag, name, status
- The filter options can be easily shared by copying the link after typing in the search terms into the searchbar.

**Instructions:**

Expand Down Expand Up @@ -218,6 +231,7 @@ The history contains all activities done directly refer to the current project.
- Tags should not contain whitespaces or any special characters except "\_", "." or "-".
- Tags can contain alphanumeric characters and can be up to 15 characters.
- Tags cannot not start or end with the special characters listed above
- Tag-Buttons in the project list of the overview page can filter projects by tag

**Instructions:**

Expand Down
107 changes: 66 additions & 41 deletions e2e-test/cypress/integration/login_spec.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
describe("Login", function() {
let routes;
let projectId, subprojectId;
const routes = {
overview: "projects",
users: "users",
notifications: "notifications",
nodes: "nodes",
projectDetails: `projects/${projectId}`,
subprojectDetails: `projects/${projectId}/${subprojectId}`,
notFound: "notfound"
};

before(function() {
cy.login();
cy.createProject("p-subp-assign", "subproject assign test").then(({ id }) => {
const projectId = id;
cy.createSubproject(projectId, "subproject assign test").then(({ id }) => {
const subprojectId = id;
cy.createProject("p-login", "login test").then(({ id }) => {
projectId = id;
cy.createSubproject(projectId, "sp-login").then(({ id }) => {
subprojectId = id;
// Logout
localStorage.setItem("state", undefined);

routes = [
"projects",
"users",
"notifications",
"nodes",
`projects/${projectId}`,
`projects/${projectId}/${subprojectId}`,
"notfound"
];
});
});
});
Expand All @@ -27,31 +26,34 @@ describe("Login", function() {
cy.visit(`/`);
});

it(`Log in and out on every route`, function() {
routes.forEach(route => {
// Login process
cy.get("#loginpage")
.should("be.visible")
.get("#username")
.should("be.visible")
.type("mstein")
.should("have.value", "mstein")
.get("#password")
.should("be.visible")
.type("test")
.should("have.value", "test")
.get("#loginbutton")
.click();
// Check if logged in correctly
cy.get("#logoutbutton").should("be.visible");
// Logout on specific route
cy.visit(`/${route}`);
cy.get("#logoutbutton")
.should("be.visible")
.click();
// Check if logged out correctly
cy.get("#loginpage").should("be.visible");
});
it(`Log in and out on overview page`, function() {
loginUi();
logout(routes.overview);
});

it(`Log in and out on users page`, function() {
loginUi();
logout(routes.users);
});
it(`Log in and out on notifications page`, function() {
loginUi();
logout(routes.notifications);
});
it(`Log in and out on nodes page`, function() {
loginUi();
logout(routes.nodes);
});
it(`Log in and out on projectDetails page`, function() {
loginUi();
logout(routes.projectDetails);
});
it(`Log in and out on subprojectDetails page`, function() {
loginUi();
logout(routes.subprojectDetails);
});
it(`Log in and out on a page that's not found`, function() {
loginUi();
logout(routes.notFound);
});

it("Reject wrong inputs", function() {
Expand All @@ -61,10 +63,33 @@ describe("Login", function() {
.type("foo")
.should("have.value", "foo");
cy.get("#password")
.should("be.visible")
.type("bar")
.should("have.value", "bar");
cy.get("#loginbutton").click();
cy.get("#password-helper-text").should("be.visible");
});
});

function logout(route) {
cy.visit(`/${route}`);
cy.get("#logoutbutton")
.should("be.visible")
.click();
// Check if logged out correctly
cy.get("#loginpage").should("be.visible");
}

function loginUi() {
cy.get("#loginpage")
.should("be.visible")
.get("#username")
.type("mstein")
.should("have.value", "mstein")
.get("#password")
.type("test")
.should("have.value", "test")
.get("#loginbutton")
.click();
// Check if logged in correctly
cy.get("#logoutbutton").should("be.visible");
}
72 changes: 0 additions & 72 deletions e2e-test/cypress/integration/navigation_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,78 +68,6 @@ describe("Navigation", function() {
});
});

it("Filter projects by display name", function() {
// Set a unique project name
const projectDisplayName = Math.floor(Math.random() * 10000000000);

// Create project which will then be displayed after filtering
cy.createProject(projectDisplayName, projectDisplayName, [])
.then(() => cy.visit(`/projects`))
.then(() => {
cy.get("[data-test=toggle-project-search]").click();
cy.get("[data-test=project-search-field]").should("be.visible");
cy.get("[data-test=project-search-field] input").type(projectDisplayName);
// Since project name is unique, there can only be one match
cy.get("[data-test*=project-card]").then(res => assert.equal(res.length, 1));

// Check the functionality of the clear button
cy.get("[data-test=clear-project-search]").click();
cy.get("[data-test=project-search-field]").should("not.be.visible");
});
});

it("Search bar is closed and reset when viewing project details", function() {
// Set a unique project name
const projectDisplayName = Math.floor(Math.random() * 10000000000);

// Create project which will then be displayed after filtering
cy.createProject(projectDisplayName, projectDisplayName, [])
.then(() => cy.visit(`/projects`))
.then(() => {
cy.get("[data-test=toggle-project-search]").click();
cy.get("[data-test=project-search-field]").should("be.visible");
cy.get("[data-test=project-search-field] input").type(projectDisplayName);
// Since project name is unique, there can only be one match
cy.get("[data-test*=project-card]").then(res => assert.equal(res.length, 1));

// Go to project
cy.get("[data-test*=project-view-button]")
.first()
.click();

cy.get("[data-test=project-search-field]").should("not.be.visible");
cy.get("[data-test=toggle-project-search]").should("be.disabled");

cy.visit("/projects");
// Search field should be empty
cy.get("[data-test=toggle-project-search]").click();
cy.get("[data-test=project-search-field] input").should("have.value", "");
});
});

it("Search bar is closed and reset when clicking on 'Main' breadcrumb", function() {
// Set a unique project name
const projectDisplayName = Math.floor(Math.random() * 10000000000);

// Create project which will then be displayed after filtering
cy.createProject(projectDisplayName, projectDisplayName, [])
.then(() => cy.visit(`/projects`))
.then(() => {
cy.get("[data-test=toggle-project-search]").click();
cy.get("[data-test=project-search-field]").should("be.visible");
cy.get("[data-test=project-search-field] input").type(projectDisplayName);
// Since project name is unique, there can only be one match
cy.get("[data-test*=project-card]").then(res => assert.equal(res.length, 1));

// Go to project
cy.get("[data-test=breadcrumb-Main]").click();

cy.get("[data-test=project-search-field]").should("not.be.visible");
cy.get("[data-test=toggle-project-search]").should("be.enabled");
// TODO: Test what happens when you click on a project
});
});

it("The notification button redirects to the notification page", function() {
cy.get("[data-test=navbar-notification-button]").should("be.visible");
cy.get("[data-test=navbar-notification-button]").click();
Expand Down
Loading