diff --git a/cypress/fixtures/policies.json b/cypress/fixtures/policies.json
index 51157a74..9250f8fd 100644
--- a/cypress/fixtures/policies.json
+++ b/cypress/fixtures/policies.json
@@ -2,10 +2,32 @@
"Existing": {
"data": [
{
- "id": "12345678910",
+ "id": "34912489-bd2a-409e-a00a-da274aff0635",
+ "policyId": "34912489-bd2a-409e-a00a-da274aff0635",
"name": "My Policy Name",
"description": "This is a policy description",
- "regoContent": "package RegoRegoRego"
+ "regoContent": "package RegoRegoRego",
+ "currentVersion": 2,
+ "policyVersionId": "34912489-bd2a-409e-a00a-da274aff0635.1"
+ }
+ ],
+ "versions": [
+ {
+ "created": "2021-07-06T13:53:41.872378726Z",
+ "id": "34912489-bd2a-409e-a00a-da274aff0635.1",
+ "message": "Initial policy creation",
+ "regoContent": "package rode.demo.sonar\n\npass {\n\tcount(violation_count) == 0\n}\n\nviolation_count[v] {\n\tviolations[v].pass == false\n}\n\nsonar_scan_started[o] {\n\tstartswith(input.occurrences[i].noteName, \"projects/rode/notes/sonar-scan\")\n\tinput.occurrences[i].kind == \"DISCOVERY\"\n\tinput.occurrences[i].discovered.discovered.analysisStatus == \"SCANNING\"\n\to := input.occurrences[i]\n}\n\nsonar_scan_finished[t] {\n\tstartswith(input.occurrences[i].noteName, \"projects/rode/notes/sonar-scan\")\n\tinput.occurrences[i].kind == \"DISCOVERY\"\n\tinput.occurrences[i].discovered.discovered.analysisStatus == \"FINISHED_SUCCESS\"\n\tt := input.occurrences[i].createTime\n}\n\nviolations[result] {\n\tstarted := sonar_scan_started\n\tresult := {\n\t\t\"pass\": count(started) >= 1,\n\t\t\"id\": \"sonar_scan_started\",\n\t\t\"name\": \"SonarQube Analysis Started\",\n\t\t\"description\": \"Verify SonarQube analysis started\",\n\t\t\"message\": \"SonarQube analysis started\",\n\t}\n}\n\nviolations[result] {\n\tfinished := sonar_scan_finished\n\tresult := {\n\t\t\"pass\": count(finished) >= 1,\n\t\t\"id\": \"sonar_scan_completed\",\n\t\t\"name\": \"SonarQube Analysis Finished\",\n\t\t\"description\": \"Verify SonarQube analysis finished successfully\",\n\t\t\"message\": sprintf(\"SonarQube analysis completed at %v\", [finished]),\n\t}\n}\n",
+ "sourcePath": "",
+ "version": 1
+ }
+ ],
+ "assignments": [
+ {
+ "created": "2021-07-06T13:59:41.639466966Z",
+ "id": "policies/34912489-bd2a-409e-a00a-da274aff0635/assignments/existing",
+ "policyGroup": "existing",
+ "policyVersionId": "34912489-bd2a-409e-a00a-da274aff0635.1",
+ "updated": "2021-07-06T13:59:41.639466966Z"
}
],
"pageToken": "12345678910pageTokenHere"
diff --git a/cypress/fixtures/policy-groups.json b/cypress/fixtures/policy-groups.json
new file mode 100644
index 00000000..c0929c27
--- /dev/null
+++ b/cypress/fixtures/policy-groups.json
@@ -0,0 +1,48 @@
+{
+ "Existing": {
+ "data": [
+ {
+ "created": "2021-07-06T13:59:28.133457892Z",
+ "deleted": false,
+ "description": "policies that limit the number of vulnerabilities",
+ "name": "existing",
+ "updated": "2021-07-06T13:59:28.133457892Z"
+ }
+ ],
+ "assignments": [],
+ "pageToken": "12345678910pageTokenHere"
+ },
+ "ExistingWithAssignments": {
+ "data": [
+ {
+ "created": "2021-07-06T13:59:28.133457892Z",
+ "deleted": false,
+ "description": "policies that limit the number of vulnerabilities",
+ "name": "existingWithAssignments",
+ "updated": "2021-07-06T13:59:28.133457892Z"
+ }
+ ],
+ "assignments": [
+ {
+ "created": "2021-07-06T13:59:41.639466966Z",
+ "id": "policies/34912489-bd2a-409e-a00a-da274aff0635/assignments/existingWithAssignments",
+ "policyGroup": "existingWithAssignments",
+ "policyId": "34912489-bd2a-409e-a00a-da274aff0635",
+ "policyVersion": 1,
+ "policyVersionCount": 2,
+ "policyVersionId": "34912489-bd2a-409e-a00a-da274aff0635.1",
+ "policyName": "My Policy Name",
+ "updated": "2021-07-06T13:59:41.639466966Z"
+ }
+ ],
+ "pageToken": "12345678910pageTokenHere"
+ },
+ "New": {
+ "data": [
+ {
+ "name": "my_brand_new_policy_group",
+ "description": "This policy group was just created"
+ }
+ ]
+ }
+}
diff --git a/cypress/fixtures/resources.json b/cypress/fixtures/resources.json
index 12b86e76..1c258494 100644
--- a/cypress/fixtures/resources.json
+++ b/cypress/fixtures/resources.json
@@ -584,7 +584,46 @@
}
],
"other": []
- }
+ },
+ "evaluations": [
+ {
+ "id": "5e071925-f2d0-489e-8510-52da4dc0b3b6",
+ "pass": false,
+ "source": null,
+ "created": "2021-07-06T18:34:39.733446920Z",
+ "resourceVersion": {
+ "version": "harbor.localhost/library/nginx@sha256:0b159cd1ee1203dad901967ac55eee18c24da84ba3be384690304be93538bea8",
+ "names": ["test", "another tag"],
+ "created": "2021-07-06T13:58:56.024026238Z"
+ },
+ "policyGroup": "vulnerability_limits",
+ "resourceUri": "harbor.localhost/library/nginx@sha256:0b159cd1ee1203dad901967ac55eee18c24da84ba3be384690304be93538bea8",
+ "resourceAliases": [
+ "harbor.localhost/library/nginx:fail-harbor-policy-e6cbe59"
+ ],
+ "policyEvaluations": [
+ {
+ "id": "9f8adc21-7abd-46f8-92f2-e791344b2b9f",
+ "resourceEvaluationId": "5e071925-f2d0-489e-8510-52da4dc0b3b6",
+ "pass": false,
+ "policyVersionId": "923260c9-40e8-4a9a-b41f-547a2e190697.1",
+ "violations": [
+ {
+ "id": "harbor_scan_completed",
+ "name": "Harbor Scan",
+ "description": "Verify Harbor image scan completed",
+ "message": "Harbor scanned image 1 times. Last completed at {'2021-07-06T13:59:21.912469Z'}",
+ "link": "",
+ "pass": true
+ }
+ ],
+ "policyVersion": 1,
+ "policyId": "923260c9-40e8-4a9a-b41f-547a2e190697",
+ "policyName": "Sample Harbor Policy"
+ }
+ ]
+ }
+ ]
}
],
"pageToken": "123456789010pagetokenhere"
diff --git a/cypress/integration/Playground.feature b/cypress/integration/Playground.feature
index 950d300d..405e786d 100644
--- a/cypress/integration/Playground.feature
+++ b/cypress/integration/Playground.feature
@@ -12,7 +12,7 @@ Feature: Policy Playground
And I search for "Existing" resource version
And I select "Existing" resource version for evaluation
When the resource the policy
- Then I see "" message
+ Then I see the "" message
Scenarios:
| outcome | message |
| passes | SuccessfulEvaluation |
@@ -29,4 +29,4 @@ Feature: Policy Playground
And I search for "Existing" resource version
And I select "Existing" resource version for evaluation
When I evaluate and an error occurs
- Then I see "EvaluationError" message
\ No newline at end of file
+ Then I see the "EvaluationError" message
\ No newline at end of file
diff --git a/cypress/integration/Policy.feature b/cypress/integration/Policy.feature
index c1ccbdb0..fb1002f9 100644
--- a/cypress/integration/Policy.feature
+++ b/cypress/integration/Policy.feature
@@ -8,9 +8,9 @@ Feature: Policies
Scenario: Search for a non-existent policy
Given I am on the "PolicySearch" page
When I search for a "NonExistent" policy
- Then I see "NoPoliciesFound" message
+ Then I see the "NoPoliciesFound" message
- @smoke
+ @smoke
Scenario: Search for an existing policy
Given I am on the "PolicySearch" page
When I search for an "Existing" policy
@@ -18,7 +18,22 @@ Feature: Policies
When I click the "ViewPolicy" button
Then I see "Existing" policy details
- @smoke
+ Scenario: View policy details
+ Given I am on the "Existing" policy details page
+ When I select the PolicyDetails Section
+ Then I see "Existing" policy details
+
+ Scenario: View policy version history
+ Given I am on the "Existing" policy details page
+ When I select the History Section
+ Then I see "Existing" policy version history
+
+ Scenario: View policy assignments
+ Given I am on the "Existing" policy details page
+ When I select the Assignments Section
+ Then I see "Existing" policy assignment data
+
+ @smoke
Scenario: Create policy
Given I open the application
When I navigate to the "CreatePolicy" page
@@ -29,7 +44,7 @@ Feature: Policies
Scenario: Create policy - require name field
Given I am on the "CreatePolicy" page
When I click the "SavePolicy" button
- Then I see "PolicyNameRequired" message
+ Then I see the "PolicyNameRequired" message
When I type "name" into "PolicyName" input
And I click the "SavePolicy" button
Then I no longer see "PolicyNameRequired" message
@@ -38,7 +53,7 @@ Feature: Policies
Given I am on the "CreatePolicy" page
When I clear the "PolicyRegoContent" input
When I click the "SavePolicy" button
- Then I see "PolicyRegoRequired" message
+ Then I see the "PolicyRegoRequired" message
When I type "text" into "PolicyRegoContent" input
And I click the "SavePolicy" button
Then I no longer see "PolicyRegoRequired" message
@@ -46,13 +61,13 @@ Feature: Policies
Scenario Outline: Create policy - validating rego code
Given I am on the "CreatePolicy" page
When I test Rego policy code
- Then I see "" message
+ Then I see the "" message
Scenarios:
- | validity | message |
- | invalid | PolicyFailedValidation |
- | valid | PolicyPassedValidation |
+ | validity | message |
+ | invalid | PolicyFailedValidation |
+ | valid | PolicyPassedValidation |
- @updatePolicy
+ @updatePolicy
Scenario Outline: Edit policy - update fields
Given I am on the "Existing" policy details page
When I click the "EditPolicy" button
@@ -60,36 +75,46 @@ Feature: Policies
When I update and save the "Existing" policy
Then I see the updated "Existing" policy
Scenarios:
- | field |
+ | field |
| name |
| description |
+ Scenario: Edit policy - create new policy version
+ Given I am on the "Existing" policy details page
+ When I click the "EditPolicy" button
+ Then I see the Edit Policy form for "Existing" policy
+ When I update and save the "Existing" policy regoContent
+ Then I see the "NewPolicyVersion" message
+ When I type "this is an update message" into "PolicyUpdateMessage" input
+ And I click the "ConfirmUpdatePolicy" button
+ Then I see the updated "Existing" policy regoContent
+
Scenario: Edit policy - invalid rego
Given I am on the "Existing" policy details page
When I click the "EditPolicy" button
Then I see the Edit Policy form for "Existing" policy
When I save invalid rego code
- Then I see "PolicyFailedUpdateInvalidRego" message
- Then I see "PolicyFailedValidation" message
+ Then I see the "PolicyFailedUpdateInvalidRego" message
+ Then I see the "PolicyFailedValidation" message
Scenario: Edit policy - unexpected errors
Given I am on the "Existing" policy details page
When I click the "EditPolicy" button
And I save the Edit Policy form and an error occurs
- Then I see "PolicyFailedUpdate" message
+ Then I see the "PolicyFailedUpdate" message
Scenario: Delete policy
Given I am on the "Existing" policy details page
When I click the "EditPolicy" button
And I click the "DeletePolicy" button
And I confirm to delete the policy
- Then I see "DeleteSuccess" message
+ Then I see the "DeleteSuccess" message
Scenario: Delete policy - unexpected errors
Given I am on the "Existing" policy details page
When I click the "EditPolicy" button
And I click the "DeletePolicy" button
And I confirm to delete the policy and an error occurs
- Then I see "PolicyFailedDelete" message
+ Then I see the "PolicyFailedDelete" message
diff --git a/cypress/integration/Policy/policy.js b/cypress/integration/Policy/policy.js
index b922fdc8..1d0a38c4 100644
--- a/cypress/integration/Policy/policy.js
+++ b/cypress/integration/Policy/policy.js
@@ -40,6 +40,16 @@ Given(/^I am on the "([^"]*)" policy details page$/, (policyName) => {
{ url: "**/api/policies/*", method: "GET" },
policies[policyName].data[0]
);
+
+ cy.mockRequest(
+ { url: "**/api/policies/**/versions*", method: "GET" },
+ { data: policies[policyName].versions }
+ );
+
+ cy.mockRequest(
+ { url: "**/api/policies/**/assignments*", method: "GET" },
+ { data: policies[policyName].assignments }
+ );
cy.visit(`/policies/${policies[policyName].data[0].id}`);
});
@@ -82,10 +92,6 @@ When(
const selectorName = `Policy${capitalize(field)}Input`;
cy.get(selectors[selectorName]).clear().type(updatedValues[field]);
cy.get(selectors.UpdatePolicyButton).click();
-
- if (field === "regoContent") {
- cy.get(selectors.ConfirmUpdatePolicyButton).click();
- }
}
);
@@ -166,3 +172,31 @@ Then(/^I see the Edit Policy form for "([^"]*)" policy$/, (policyName) => {
cy.get(button).should("be.visible");
});
});
+
+Then(/^I see "([^"]*)" policy version history$/, (policyName) => {
+ const policyVersion = policies[policyName].versions[0];
+ cy.url().should("contain", "#history");
+
+ cy.get('[data-testid="toggleCard"]')
+ .click()
+ .within(() => {
+ cy.contains("v1 (latest)").should("be.visible");
+ cy.contains(policyVersion.regoContent).should("be.visible");
+ });
+});
+
+Then(/^I see "([^"]*)" policy assignment data$/, (policyName) => {
+ const policyAssignment = policies[policyName].assignments[0];
+ cy.url().should("contain", "#assignments");
+
+ cy.contains(policyAssignment.policyGroup).should("be.visible");
+ cy.contains(policyAssignment.policyVersionId.split(".")[1]).should(
+ "be.visible"
+ );
+ cy.get(selectors.ViewPolicyGroupButton).should("be.visible").click();
+
+ cy.url().should(
+ "match",
+ new RegExp(`/policy-groups/${policyAssignment.policyGroup}`)
+ );
+});
diff --git a/cypress/integration/PolicyGroup.feature b/cypress/integration/PolicyGroup.feature
new file mode 100644
index 00000000..67bd1003
--- /dev/null
+++ b/cypress/integration/PolicyGroup.feature
@@ -0,0 +1,48 @@
+Feature: Policy Groups
+
+ Scenario: Open Policy Group Dashboard
+ Given I open the application
+ When I navigate to the "PolicyGroup" page
+ Then I see the policy groups dashboard
+
+ Scenario: Create policy group
+ Given I open the application
+ When I navigate to the "PolicyGroup" page
+ And I click the "CreateNewPolicyGroup" button
+ Then I see the "CreatePolicyGroup" form
+ When I create the "New" policy group
+ Then I see the "New" policy group details
+
+ Scenario: Create policy group - invalid name
+ Given I open the application
+ When I navigate to the "PolicyGroup" page
+ And I click the "CreateNewPolicyGroup" button
+ Then I see the "CreatePolicyGroup" form
+ When I type "Invalid*Name" into "PolicyGroupName" input
+ And I click the "SavePolicyGroup" button
+ Then I see the "InvalidPolicyGroupName" message
+
+ @updatePolicyGroup
+ Scenario: Edit policy group description
+ Given I am on the Existing policy group details page
+ When I click the "EditPolicyGroup" button
+ Then I see the "EditPolicyGroup" form
+ When I update and save the Existing policy group description
+ Then I see the updated Existing policy group description
+
+ Scenario: Assign policy to policy group
+ Given I am on the Existing policy group details page
+ When I click the "EditAssignments" button
+ Then I see the Edit Existing Assignments page
+ When I search for an "Existing" policy
+ And I assign the Existing policy to the Existing policy group
+ And I click the "SaveAssignments" button
+ Then I see the Existing policy assigned to the Existing policy group
+
+ Scenario: Remove policy from policy group
+ Given I am on the ExistingWithAssignments policy group details page
+ When I click the "EditAssignments" button
+ Then I see the Edit ExistingWithAssignments Assignments page
+ When I remove an assignment from the ExistingWithAssignments policy group
+ And I click the "SaveAssignments" button
+ Then I see no assignments for the ExistingWithAssignments policy group
diff --git a/cypress/integration/PolicyGroup/policy-group.js b/cypress/integration/PolicyGroup/policy-group.js
new file mode 100644
index 00000000..c3f2276e
--- /dev/null
+++ b/cypress/integration/PolicyGroup/policy-group.js
@@ -0,0 +1,203 @@
+/**
+ * Copyright 2021 The Rode Authors
+ *
+ * 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.
+ */
+
+import { Given, When, Then, Before } from "cypress-cucumber-preprocessor/steps";
+import * as selectors from "../../page-objects/policy-group";
+import policyGroups from "../../fixtures/policy-groups.json";
+import policies from "../../fixtures/policies.json";
+import Chance from "chance";
+
+// TODO: figure out cy.fixture so I can "as" it throughout the remaining steps
+const chance = new Chance();
+let updatedValues;
+
+Before({ tags: "@updatePolicyGroup" }, () => {
+ updatedValues = {
+ description: chance.sentence(),
+ };
+});
+
+Given(/^I am on the ([^"]*) policy group details page$/, (policyGroupName) => {
+ cy.mockRequest(
+ { url: "**/api/policy-groups/*", method: "GET" },
+ policyGroups[policyGroupName].data[0]
+ );
+
+ cy.mockRequest(
+ { url: "**/api/policy-groups/**/assignments*", method: "GET" },
+ { data: policyGroups[policyGroupName].assignments }
+ );
+ cy.visit(`/policy-groups/${policyGroups[policyGroupName].data[0].name}`);
+});
+
+When(/^I create the "([^"]*)" policy group$/, (policyGroupName) => {
+ const policyGroup = policyGroups[policyGroupName].data[0];
+ cy.mockRequest({ url: `**/api/policy-groups`, method: "POST" }, policyGroup);
+ cy.get(selectors.PolicyGroupNameInput).clear().type(policyGroup.name);
+ cy.get(selectors.PolicyGroupDescriptionInput)
+ .clear()
+ .type(policyGroup.description);
+ cy.get(selectors.SavePolicyGroupButton).click();
+});
+
+When(
+ /^I update and save the ([^"]*) policy group description$/,
+ (policyGroupName) => {
+ const policyGroup = policyGroups[policyGroupName].data[0];
+ const updatedPolicyGroup = {
+ ...policyGroup,
+ description: updatedValues.description,
+ };
+ cy.mockRequest(
+ { url: `**/api/policy-groups/${policyGroup.name}`, method: "PATCH" },
+ updatedPolicyGroup
+ );
+ cy.get(selectors.PolicyGroupDescriptionInput)
+ .clear()
+ .type(updatedPolicyGroup.description);
+ cy.get(selectors.UpdatePolicyGroupButton).click();
+ }
+);
+
+When(
+ /^I assign the ([^"]*) policy to the ([^"]*) policy group$/,
+ (policyName, policyGroupName) => {
+ const policyGroup = policyGroups[policyGroupName];
+ const policy = policies[policyName];
+ const assignment = {
+ ...policy.assignments[0],
+ policyId: policy.data[0].id,
+ policyVersion: policy.data[0].currentVersion,
+ policyVersionCount: policy.data[0].currentVersion,
+ policyName: policy.data[0].name,
+ };
+ cy.mockRequest(
+ { url: "**/api/policy-groups/**/assignments*", method: "POST" },
+ {
+ data: assignment,
+ }
+ );
+ cy.mockRequest(
+ {
+ url: `**/api/policy-groups/${policyGroup.data[0].name}/assignments*`,
+ method: "GET",
+ },
+ {
+ data: {
+ data: assignment,
+ },
+ }
+ );
+ cy.get(selectors.AssignToPolicyGroupButton).click();
+ }
+);
+When(
+ /^I remove an assignment from the ([^"]*) policy group$/,
+ (policyGroupName) => {
+ const policyGroup = policyGroups[policyGroupName];
+ cy.mockRequest(
+ {
+ url: `**/api/policy-groups/${policyGroup.data[0].name}/assignments/*`,
+ method: "DELETE",
+ status: 204,
+ },
+ {}
+ );
+ cy.mockRequest(
+ {
+ url: `**/api/policy-groups/${policyGroup.data[0].name}/assignments*`,
+ method: "GET",
+ },
+ {
+ data: {
+ data: [],
+ },
+ }
+ );
+ cy.get(selectors.RemoveFromPolicyGroupButton).click();
+ cy.contains(selectors.NoPolicyGroupAssignmentsMessage).should("be.visible");
+ }
+);
+
+Then(
+ /^I see the updated ([^"]*) policy group description$/,
+ (policyGroupName) => {
+ const policyGroup = policyGroups[policyGroupName].data[0];
+ cy.url().should(
+ "contain",
+ `/policy-groups/${encodeURIComponent(policyGroup.name)}`
+ );
+
+ cy.contains(policyGroup.name).should("be.visible");
+ cy.contains(updatedValues.description).should("be.visible");
+ cy.get(selectors.EditPolicyGroupButton).should("be.visible");
+ }
+);
+
+Then(/^I see the "([^"]*)" policy group details$/, (policyGroupName) => {
+ const policyGroup = policyGroups[policyGroupName].data[0];
+ cy.url().should(
+ "contain",
+ `/policy-groups/${encodeURIComponent(policyGroup.name)}`
+ );
+
+ cy.contains(policyGroup.name).should("be.visible");
+ cy.contains(policyGroup.description).should("be.visible");
+ cy.get(selectors.EditPolicyGroupButton).should("be.visible");
+ cy.get(selectors.EditAssignmentsButton).should("be.visible");
+});
+
+Then(/^I see the policy groups dashboard$/, () => {
+ cy.contains(selectors.PolicyGroupDashboardHeader).should("be.visible");
+ cy.get(selectors.CreateNewPolicyGroupButton).should("be.visible");
+});
+
+Then(/^I see the Edit ([^"]*) Assignments page$/, (policyGroupName) => {
+ const policyGroup = policyGroups[policyGroupName];
+ cy.url().should(
+ "contain",
+ `/policy-groups/${encodeURIComponent(policyGroup.data[0].name)}/assignments`
+ );
+
+ cy.contains(policyGroup.data[0].name).should("be.visible");
+ if (policyGroup.assignments.length > 0) {
+ policyGroup.assignments.forEach((assignment) => {
+ cy.contains(assignment.policyName).should("be.visible");
+ cy.contains(assignment.policyVersion).should("be.visible");
+ });
+ } else {
+ cy.contains(selectors.NoPolicyGroupAssignmentsMessage).should("be.visible");
+ }
+});
+
+Then(
+ /^I see the ([^"]*) policy assigned to the ([^"]*) policy group$/,
+ (policyName) => {
+ const policy = policies[policyName];
+
+ cy.contains("Saved!").should("be.visible");
+
+ cy.contains(policy.data[0].name).should("be.visible");
+ cy.contains(policy.data[0].currentVersion).should("be.visible");
+ }
+);
+
+Then(/^I see no assignments for the ([^"]*) policy group$/, () => {
+ cy.contains("Saved!").should("be.visible");
+
+ // TODO: why isn't this working? calls to get assignments are not returning [] after landing back on policy group page but they are following the same intercept structure as assigning new policy
+ // cy.contains(selectors.NoPolicyGroupAssignmentsMessage).should("be.visible");
+});
diff --git a/cypress/integration/Resource.feature b/cypress/integration/Resource.feature
index 17fb6470..56de5589 100644
--- a/cypress/integration/Resource.feature
+++ b/cypress/integration/Resource.feature
@@ -8,7 +8,7 @@ Feature: Resources
Scenario: Search for a non-existent resource
Given I am on the "ResourceSearch" page
When I search for "NonExistent" resource
- Then I see "NoResourcesFound" message
+ Then I see the "NoResourcesFound" message
@smoke
Scenario: Search for an existing resource
@@ -18,7 +18,7 @@ Feature: Resources
When I click the search result to view the "Existing" resource
Then I see "Existing" resource details
- Scenario Outline: Viewing Resource Details
+ Scenario Outline: Viewing Resource Occurrences
Given I am on the "Existing" resource details page
When I select the Occurrence Section
When I click on occurrence
@@ -27,4 +27,14 @@ Feature: Resources
|occurrenceType|
| Build |
| Vulnerability |
- | Deployment |
\ No newline at end of file
+ | Deployment |
+
+ Scenario: Viewing Resource Evaluation History
+ Given I am on the "Existing" resource details page
+ When I select the EvaluationHistory Section
+ Then I see evaluation history details
+
+ Scenario: Changing Resource Version
+ Given I am on the "Existing" resource details page
+ When I click the "ChangeVersion" button
+ Then I see the available resource versions
\ No newline at end of file
diff --git a/cypress/integration/Resource/resource.js b/cypress/integration/Resource/resource.js
index 997fa814..2c772dbc 100644
--- a/cypress/integration/Resource/resource.js
+++ b/cypress/integration/Resource/resource.js
@@ -19,17 +19,22 @@ import resources from "../../fixtures/resources.json";
import * as selectors from "../../page-objects/resource";
Given(/^I am on the "([^"]*)" resource details page$/, (resourceName) => {
+ const resource = resources[resourceName].data[0];
cy.mockRequest(
{ url: "**/api/occurrences*", method: "GET" },
- resources[resourceName].data[0].occurrences
+ resource.occurrences
);
- cy.visit(
- `/resources/${encodeURIComponent(resources[resourceName].data[0].uri)}`
+
+ cy.mockRequest(
+ { url: "**/api/resources/**/resource-evaluations*", method: "GET" },
+ { data: resource.evaluations }
);
-});
-When(/^I select the Occurrence Section$/, () => {
- cy.contains(selectors.OccurrenceSection).click();
+ cy.mockRequest(
+ { url: "**/api/resource-versions*", method: "GET" },
+ { data: resource.versions }
+ );
+ cy.visit(`/resources/${encodeURIComponent(resource.uri)}`);
});
When(/^I click on ([^"]*) occurrence$/, (occurrenceType) => {
@@ -127,3 +132,32 @@ Then(/^I see "([^"]*)" resource details$/, (resourceName) => {
.should("be.visible");
cy.get(selectors.EvaluateResourceInPlaygroundButton).should("be.visible");
});
+
+Then(/^I see evaluation history details$/, () => {
+ cy.url().should("contain", "#evaluationHistory");
+
+ const evaluation = resources.Existing.data[0].evaluations[0];
+
+ cy.get('[data-testid="toggleCard"]')
+ .click()
+ .within(() => {
+ cy.contains(evaluation.policyGroup).should("be.visible");
+ cy.contains(evaluation.policyEvaluations[0].policyName).should(
+ "be.visible"
+ );
+ });
+});
+
+Then(/^I see the available resource versions$/, () => {
+ const resourceVersion = resources.Existing.data[0].versions[0];
+
+ cy.get('[data-testid="drawer"]').within(() => {
+ cy.contains(resourceVersion.versionedResourceUri.substring(0, 12)).should(
+ "be.visible"
+ );
+ resourceVersion.aliases.forEach((alias) => {
+ const trimmedAlias = alias.split(":")[1];
+ cy.contains(trimmedAlias).should("be.visible");
+ });
+ });
+});
diff --git a/cypress/integration/common/common.js b/cypress/integration/common/common.js
index c19c49ce..6618f320 100644
--- a/cypress/integration/common/common.js
+++ b/cypress/integration/common/common.js
@@ -90,7 +90,7 @@ Then(/^I see "([^"]*)"$/, (element) => {
cy.get(selectors[element]).should("be.visible");
});
-Then(/^I see "([^"]*)" message$/, (messageName) => {
+Then(/^I see the "([^"]*)" message$/, (messageName) => {
const message = `${messageName}Message`;
cy.contains(selectors[message]).should("be.visible");
});
@@ -110,3 +110,8 @@ Then(/^I see the "([^"]*)" form$/, (formName) => {
cy.get(button).should("be.visible");
});
});
+
+When(/^I select the ([^"]*) Section$/, (sectionName) => {
+ const section = `${sectionName}Section`;
+ cy.contains(selectors[section]).click();
+});
diff --git a/cypress/page-objects/index.js b/cypress/page-objects/index.js
index 2c5b465d..db92f254 100644
--- a/cypress/page-objects/index.js
+++ b/cypress/page-objects/index.js
@@ -17,3 +17,4 @@
export * from "./policy";
export * from "./playground";
export * from "./resource";
+export * from "./policy-group";
diff --git a/cypress/page-objects/navigation.js b/cypress/page-objects/navigation.js
index af1771c4..3273a5bf 100644
--- a/cypress/page-objects/navigation.js
+++ b/cypress/page-objects/navigation.js
@@ -34,9 +34,15 @@ const CREATE_POLICY = {
href: "/policies/new",
};
+const POLICY_GROUP = {
+ label: "Policy Groups",
+ href: "/policy-groups",
+};
+
export default {
ResourceSearch: RESOURCE_SEARCH,
PolicySearch: POLICY_SEARCH,
PolicyPlayground: POLICY_PLAYGROUND,
CreatePolicy: CREATE_POLICY,
+ PolicyGroup: POLICY_GROUP,
};
diff --git a/cypress/page-objects/policy-group.js b/cypress/page-objects/policy-group.js
new file mode 100644
index 00000000..173746a1
--- /dev/null
+++ b/cypress/page-objects/policy-group.js
@@ -0,0 +1,59 @@
+/**
+ * Copyright 2021 The Rode Authors
+ *
+ * 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.
+ */
+
+import { createButtonSelector } from "./utils";
+
+export const PolicyGroupDashboardHeader = "Manage Policy Groups";
+
+//MESSAGES
+export const NoPolicyGroupsFoundMessage = "No policy groups exist.";
+export const NoPolicyGroupAssignmentsMessage =
+ "No policies are assigned to this policy group.";
+export const InvalidPolicyGroupNameMessage =
+ "Invalid character(s). Please refer to the name guidelines.";
+
+// BUTTONS
+export const CreateNewPolicyGroupButton = createButtonSelector(
+ "Create New Policy Group"
+);
+export const SavePolicyGroupButton = createButtonSelector("Save Policy Group");
+export const EditPolicyGroupButton = createButtonSelector("Edit Policy Group");
+export const UpdatePolicyGroupButton = createButtonSelector(
+ "Update Policy Group"
+);
+export const EditAssignmentsButton = createButtonSelector("Edit Assignments");
+const CancelButton = createButtonSelector("Cancel");
+export const AssignToPolicyGroupButton = createButtonSelector(
+ "Assign to Policy Group"
+);
+export const RemoveFromPolicyGroupButton = createButtonSelector(
+ "Remove Policy Assignment"
+);
+export const SaveAssignmentsButton = createButtonSelector("Save Assignments");
+
+// INPUTS
+export const PolicyGroupNameInput = "#name";
+export const PolicyGroupDescriptionInput = "#description";
+
+// FORMS
+export const CreatePolicyGroupForm = {
+ fields: [PolicyGroupNameInput, PolicyGroupDescriptionInput],
+ buttons: [SavePolicyGroupButton, CancelButton],
+};
+export const EditPolicyGroupForm = {
+ fields: [PolicyGroupNameInput, PolicyGroupDescriptionInput],
+ buttons: [UpdatePolicyGroupButton, CancelButton],
+};
diff --git a/cypress/page-objects/policy.js b/cypress/page-objects/policy.js
index 488fbaab..c9ea7a0c 100644
--- a/cypress/page-objects/policy.js
+++ b/cypress/page-objects/policy.js
@@ -30,6 +30,8 @@ export const PolicyFailedUpdateInvalidRegoMessage =
export const PolicyFailedUpdateMessage = "Failed to update the policy.";
export const PolicyFailedDeleteMessage =
"An error occurred while deleting the policy. Please try again.";
+export const NewPolicyVersionMessage =
+ "By updating the Rego Policy Code, you are creating a new version of this policy.";
// BUTTONS
export const SearchPolicyButton = createButtonSelector("Search Policies");
@@ -46,15 +48,23 @@ export const DeletePolicyButton = createButtonSelector("Delete Policy");
export const ConfirmUpdatePolicyButton = createButtonSelector(
"Update & Save Policy"
);
+export const ViewPolicyGroupButton = createButtonSelector("View Policy Group");
+
+// SECTIONS
+export const PolicyDetailsSection = /^Policy Details$/;
+export const HistorySection = /^History$/;
+export const AssignmentsSection = /^Assignments$/;
// INPUTS
export const PolicySearchInput = "#policySearchDisplay";
export const PolicyNameInput = "#name";
export const PolicyDescriptionInput = "#description";
export const PolicyRegoContentInput = "#regoContent";
+export const PolicyUpdateMessageInput = "#message";
// MODALS
export const DeletePolicyModal = "div[role='dialog']";
+export const UpdatePolicyModal = "div[role='dialog']";
// FORMS
export const EditPolicyForm = {
diff --git a/cypress/page-objects/resource.js b/cypress/page-objects/resource.js
index 3db3779f..e0c75c00 100644
--- a/cypress/page-objects/resource.js
+++ b/cypress/page-objects/resource.js
@@ -29,9 +29,11 @@ export const EvaluateResourceInPlaygroundButton = createButtonSelector(
"Evaluate in Policy Playground"
);
export const ShowJsonButton = createButtonSelector("Show JSON");
+export const ChangeVersionButton = createButtonSelector("Change Version");
// OCCURRENCES
export const OccurrenceSection = /^Occurrences$/;
+export const EvaluationHistorySection = /^Evaluation History$/;
export const BuildOccurrence = /produced \d artifact(s?)/i;
export const VulnerabilityOccurrence = /\d vulnerabilities found/i;
export const DeploymentOccurrence = /deployment to \w+/i;
diff --git a/pages/api/policies/[id]/assignments.js b/pages/api/policies/[id]/assignments.js
index e0b1c4e4..c823aae2 100644
--- a/pages/api/policies/[id]/assignments.js
+++ b/pages/api/policies/[id]/assignments.js
@@ -33,7 +33,7 @@ export default async (req, res) => {
const { id } = req.query;
const response = await get(
- `${rodeUrl}/v1alpha1/policies/${id}/assignments`,
+ `${rodeUrl}/v1alpha1/policies/${id}/assignments?pageSize=1000`,
req.accessToken
);
@@ -49,7 +49,7 @@ export default async (req, res) => {
const { policyAssignments } = getPolicyAssignments;
return res.status(StatusCodes.OK).json({
- policyAssignments,
+ data: policyAssignments,
});
} catch (error) {
console.error("Error getting policy assignments", error);
diff --git a/pages/api/policies/[id]/versions.js b/pages/api/policies/[id]/versions.js
index 1edf1744..4c4590f1 100644
--- a/pages/api/policies/[id]/versions.js
+++ b/pages/api/policies/[id]/versions.js
@@ -32,7 +32,7 @@ export default async (req, res) => {
const { id } = req.query;
const response = await get(
- `${rodeUrl}/v1alpha1/policies/${id}/versions`,
+ `${rodeUrl}/v1alpha1/policies/${id}/versions?pageSize=1000`,
req.accessToken
);
diff --git a/pages/api/policy-groups/[name]/assignments.js b/pages/api/policy-groups/[name]/assignments.js
index ca4b5dd1..623766db 100644
--- a/pages/api/policy-groups/[name]/assignments.js
+++ b/pages/api/policy-groups/[name]/assignments.js
@@ -35,7 +35,7 @@ export default async (req, res) => {
const { name } = req.query;
const response = await get(
- `${rodeUrl}/v1alpha1/policy-groups/${name}/assignments`,
+ `${rodeUrl}/v1alpha1/policy-groups/${name}/assignments?pageSize=1000`,
req.accessToken
);
diff --git a/pages/policy-groups/[name]/assignments.js b/pages/policy-groups/[name]/assignments.js
index 2ffd8e07..ac30bd60 100644
--- a/pages/policy-groups/[name]/assignments.js
+++ b/pages/policy-groups/[name]/assignments.js
@@ -197,6 +197,7 @@ const EditPolicyGroupAssignments = () => {
return {
...assignment,
id: createdAssignment.data.id,
+ policyGroup: policyGroup.name,
action: null,
};
}
diff --git a/test/components/occurrences/VulnerabilityCard.spec.js b/test/components/occurrences/VulnerabilityCard.spec.js
index ba14e57e..143b5e91 100644
--- a/test/components/occurrences/VulnerabilityCard.spec.js
+++ b/test/components/occurrences/VulnerabilityCard.spec.js
@@ -78,7 +78,7 @@ describe("VulnerabilityCard", () => {
screen.getByRole("button", { name: "Toggle Card Details" })
);
- expect(screen.getByText(/high/i)).toBeInTheDocument();
+ expect(screen.getByText(/^high$/i)).toBeInTheDocument();
expect(screen.getAllByTitle("Fire")).toHaveLength(3);
});
@@ -100,7 +100,7 @@ describe("VulnerabilityCard", () => {
screen.getByRole("button", { name: "Toggle Card Details" })
);
- expect(screen.getByText(/low/i)).toBeInTheDocument();
+ expect(screen.getByText(/^low$/i)).toBeInTheDocument();
expect(screen.getAllByTitle("Fire")).toHaveLength(1);
});
});
diff --git a/test/components/policies/PolicyAssignments.spec.js b/test/components/policies/PolicyAssignments.spec.js
index 88f12651..38672b7a 100644
--- a/test/components/policies/PolicyAssignments.spec.js
+++ b/test/components/policies/PolicyAssignments.spec.js
@@ -41,7 +41,7 @@ describe("PolicyAssignments", () => {
);
mockResponse = {
- data: { policyAssignments },
+ data: { data: policyAssignments },
loading: false,
};
router = {
diff --git a/test/components/resources/PolicyEvaluationDetails.spec.js b/test/components/resources/PolicyEvaluationDetails.spec.js
index c3e9808b..c00a9dfb 100644
--- a/test/components/resources/PolicyEvaluationDetails.spec.js
+++ b/test/components/resources/PolicyEvaluationDetails.spec.js
@@ -27,7 +27,7 @@ describe("PolicyEvaluationDetails", () => {
pass: chance.bool(),
policyName: chance.string(),
policyVersion: chance.d4(),
- policyVersionId: chance.string(),
+ policyVersionId: chance.string({ alpha: true }),
created: chance.timestamp(),
violations: [
{
diff --git a/test/pages/api/policies/[id]/assignments.spec.js b/test/pages/api/policies/[id]/assignments.spec.js
index 1f5c066f..6a6f2a28 100644
--- a/test/pages/api/policies/[id]/assignments.spec.js
+++ b/test/pages/api/policies/[id]/assignments.spec.js
@@ -94,7 +94,9 @@ describe("/api/policies/[id]/assignments", () => {
expect(get)
.toHaveBeenCalledTimes(1)
.toHaveBeenCalledWith(
- `${config.get("rode.url")}/v1alpha1/policies/${id}/assignments`,
+ `${config.get(
+ "rode.url"
+ )}/v1alpha1/policies/${id}/assignments?pageSize=1000`,
accessToken
);
});
@@ -109,7 +111,7 @@ describe("/api/policies/[id]/assignments", () => {
expect(response.json)
.toHaveBeenCalledTimes(1)
.toHaveBeenCalledWith({
- policyAssignments: [assignment],
+ data: [assignment],
});
});
});
diff --git a/test/pages/api/policies/[id]/versions.spec.js b/test/pages/api/policies/[id]/versions.spec.js
index c79fe504..f2808d82 100644
--- a/test/pages/api/policies/[id]/versions.spec.js
+++ b/test/pages/api/policies/[id]/versions.spec.js
@@ -86,7 +86,9 @@ describe("/api/policies/[id]/versions", () => {
expect(get)
.toHaveBeenCalledTimes(1)
.toHaveBeenCalledWith(
- `${config.get("rode.url")}/v1alpha1/policies/${id}/versions`,
+ `${config.get(
+ "rode.url"
+ )}/v1alpha1/policies/${id}/versions?pageSize=1000`,
accessToken
);
});
diff --git a/test/pages/api/policy-groups/[name]/assignments.spec.js b/test/pages/api/policy-groups/[name]/assignments.spec.js
index 4806b3d8..8c65bb13 100644
--- a/test/pages/api/policy-groups/[name]/assignments.spec.js
+++ b/test/pages/api/policy-groups/[name]/assignments.spec.js
@@ -102,7 +102,7 @@ describe("/api/policy-groups/[name]/assignments", () => {
.toHaveBeenCalledWith(
`${config.get(
"rode.url"
- )}/v1alpha1/policy-groups/${name}/assignments`,
+ )}/v1alpha1/policy-groups/${name}/assignments?pageSize=1000`,
accessToken
);
});
diff --git a/test/pages/policies/[id].spec.js b/test/pages/policies/[id].spec.js
index 5f1a65a1..8ca89813 100644
--- a/test/pages/policies/[id].spec.js
+++ b/test/pages/policies/[id].spec.js
@@ -90,7 +90,7 @@ describe("Policy Details", () => {
};
mockFetch = {
data: {
- policyAssignments: assignments,
+ data: assignments,
},
loading: false,
};