Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
b548358
Allow empty detail fields via bulk edit
Bazinator Apr 5, 2026
cf928a1
Update src/main/resources/static/js/workspace/test-case-details.js
kendymann Apr 6, 2026
ddd9c24
implemented testcase creation and deletion
angelo-yap Apr 6, 2026
edc7876
implemented testcase creation and deletion
angelo-yap Apr 6, 2026
12074b6
implemented suggested fixes
angelo-yap Apr 6, 2026
22cbc3f
fix deletion to be safer, fix test cases
angelo-yap Apr 6, 2026
b9d1c29
updated scroll bar formatting to be consistent with directory scrollbar
angelo-yap Apr 6, 2026
587ec9f
fix: folder edit cancel empty state
Bazinator Apr 6, 2026
9db44ad
fix: folder edit click target
Bazinator Apr 6, 2026
9b49e1a
fix: bulk edit persisted change metrics
Bazinator Apr 6, 2026
ad0568f
Merge pull request #68 from angelo-yap/61-bugfix-single-edit-page
Bazinator Apr 6, 2026
52f6ae3
Dynamically refreshing filters
Bazinator Apr 6, 2026
430e7aa
Updated refresh logic to map instead of hardcoding!
Bazinator Apr 6, 2026
c602e6c
Merge pull request #73 from angelo-yap/70-bugfix-headerfilter-option
kendymann Apr 6, 2026
e1be9b7
implemented testcase creation and deletion
angelo-yap Apr 6, 2026
bf7fcd3
implemented testcase creation and deletion
angelo-yap Apr 6, 2026
95ef529
implemented suggested fixes
angelo-yap Apr 6, 2026
312466e
fix deletion to be safer, fix test cases
angelo-yap Apr 6, 2026
7ce5b44
updated scroll bar formatting to be consistent with directory scrollbar
angelo-yap Apr 6, 2026
865c665
Merge branch '65-new-testcase-creation' of https://github.com/angelo-…
kendymann Apr 6, 2026
13d0d59
added test step add/delete, fixed status and priority fields
angelo-yap Apr 6, 2026
f8cb096
cannonized status, fixed status methods, updated createdOn on testcas…
angelo-yap Apr 7, 2026
07a0600
suggested fixed
angelo-yap Apr 7, 2026
0e22007
fix testcase view not updating on folder move
angelo-yap Apr 7, 2026
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
1 change: 1 addition & 0 deletions .context/STATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ Recent delivery:
- folder delete confirmation is now a centered modal with blue-tinted backdrop, theme-aware styling, and keyboard focus trapping
- landing/login/register now use the same gray dark surface family as workspace for visual consistency
- workspace now uses a fixed-height split-pane shell with independent sidebar/grid scrolling (page scroll disabled in workspace view)
- workspace filter metadata now refreshes in place after successful workspace bulk edits, successful imports, and when returning from a successfully edited test case details page; current filter/page context is preserved where possible while component/status/tag options are re-fetched from metadata APIs

Primary active goals:

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.formswim.teststream.bulk.controllers;

import java.util.Optional;
import java.util.stream.Collectors;

import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.HttpStatus;
Expand Down Expand Up @@ -116,10 +117,20 @@ public ResponseEntity<BulkEditResult> apiBulkEditTestCases(@Valid @RequestBody B
String normalizedStatusValue = normalizeTrimmed(request.getStatusValue());
request.setFindText(normalizedFindText);
request.setStatusValue(normalizedStatusValue);
request.setFieldValues(request.getFieldValues().entrySet().stream()
.filter(entry -> entry.getKey() != null)
.collect(Collectors.toMap(
entry -> entry.getKey().trim(),
entry -> entry.getValue() == null ? "" : entry.getValue(),
(left, right) -> right,
java.util.LinkedHashMap::new
)));

boolean hasTextOperation = !normalizedFindText.isBlank();
boolean hasStatusOperation = !normalizedStatusValue.isBlank();
if (!hasTextOperation && !hasStatusOperation) {
boolean hasDirectSetOperation = !request.getFieldValues().isEmpty();
boolean hasStepMutationOperation = !request.getStepMutations().isEmpty();
if (!hasTextOperation && !hasStatusOperation && !hasDirectSetOperation && !hasStepMutationOperation) {
return ResponseEntity.badRequest().build();
}

Expand Down
117 changes: 117 additions & 0 deletions src/main/java/com/formswim/teststream/bulk/dto/BulkEditRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import jakarta.validation.constraints.NotNull;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
* Request payload for PATCH /api/testcases/bulk-edit.
Expand All @@ -27,6 +29,10 @@ public class BulkEditRequest {

private String statusValue;

private Map<String, String> fieldValues = new LinkedHashMap<>();

private List<StepMutation> stepMutations = new ArrayList<>();

/**
* Returns requested work keys as a non-null list.
*/
Expand Down Expand Up @@ -104,4 +110,115 @@ public String getStatusValue() {
public void setStatusValue(String statusValue) {
this.statusValue = statusValue;
}

/**
* Optional direct-assignment values keyed by canonical field names.
*/
public Map<String, String> getFieldValues() {
if (fieldValues == null) {
fieldValues = new LinkedHashMap<>();
}
return fieldValues;
}

public void setFieldValues(Map<String, String> fieldValues) {
this.fieldValues = fieldValues == null ? new LinkedHashMap<>() : new LinkedHashMap<>(fieldValues);
}

/**
* Optional list of step-level mutations applied in order for each selected test case.
*/
public List<StepMutation> getStepMutations() {
if (stepMutations == null) {
stepMutations = new ArrayList<>();
}
return stepMutations;
}

public void setStepMutations(List<StepMutation> stepMutations) {
this.stepMutations = stepMutations == null ? new ArrayList<>() : new ArrayList<>(stepMutations);
}

public static class StepMutation {

private String op;

private Integer stepNumber;

private Integer afterStepNumber;

private String stepSummary;

private String testData;

private String expectedResult;

private String field;

private String value;

public String getOp() {
return op;
}

public void setOp(String op) {
this.op = op;
}

public Integer getStepNumber() {
return stepNumber;
}

public void setStepNumber(Integer stepNumber) {
this.stepNumber = stepNumber;
}

public Integer getAfterStepNumber() {
return afterStepNumber;
}

public void setAfterStepNumber(Integer afterStepNumber) {
this.afterStepNumber = afterStepNumber;
}

public String getStepSummary() {
return stepSummary;
}

public void setStepSummary(String stepSummary) {
this.stepSummary = stepSummary;
}

public String getTestData() {
return testData;
}

public void setTestData(String testData) {
this.testData = testData;
}

public String getExpectedResult() {
return expectedResult;
}

public void setExpectedResult(String expectedResult) {
this.expectedResult = expectedResult;
}

public String getField() {
return field;
}

public void setField(String field) {
this.field = field;
}

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}
}
}
Loading
Loading