js/test-management-scenarios/status-management.js (353 lines of code) (raw):
/**
* This is a template for a rule that defines a state-machine per issue type.
* This rule defines the set of transitions that are allowed for a custom field.
* You can define alternative transition paths based on the current value in
* a second field, commonly the field that stores the issue type.
*
* For details, read the Quick Start Guide:
* https://www.jetbrains.com/help/youtrack/incloud/2020.3/Quick-Start-Guide-Workflows-JS.html
*/
var entities = require('@jetbrains/youtrack-scripting-api/entities');
var workflow = require('@jetbrains/youtrack-scripting-api/workflow');
exports.rule = entities.Issue.stateMachine({
title: 'status-management',
stateFieldName: 'Status',
typeFieldName: 'Type',
defaultMachine: {
'No Run': {
initial: true,
transitions: {
'Failed': {
targetState: 'Failed',
action: function(ctx) {
var issue = ctx.issue;
if (!issue.links['subtask of'].isEmpty()) {
var parent = issue.links['subtask of'].first();
var TestRunList = parent.links[ctx.Subtask.outward];
var resultSet = null;
var isPassing = true;
TestRunList.forEach(function(v) {
if (v.Status.name == ctx.Status.Failed.name) {
isPassing = false;
} else if ((v.Status.name == ctx.Status.InProgress.name) && (v.id !== issue.id)) {
resultSet = v;
}
});
if (resultSet) {
var otherIssueLink = '<a href="' + resultSet.url + '"> ' + resultSet.id + '</a>';
var message = 'Switch to next open test in current Test Run' + otherIssueLink + '.';
workflow.message(message);
// Updating Test Run Status
parent.fields["Status"] = ctx.Status.Failing;
} else {
parent.fields["Status"] = ctx.Status.Failed;
}
}
}
},
'Passed': {
guard: function(ctx) {
var issue = ctx.issue;
// reconsider condition
return !issue.isChanged('project') && !issue.becomesReported && issue.isReported && (issue.Type.name == ctx.Type.TestExecution.name);
},
targetState: 'Passed',
action: function(ctx) {
var issue = ctx.issue;
if (!issue.links['subtask of'].isEmpty()) {
var parent = issue.links['subtask of'].first();
var TestRunList = parent.links[ctx.Subtask.outward];
var resultSet = null;
var isPassing = true;
TestRunList.forEach(function(v) {
if (v.Status.name == ctx.Status.Failed.name) {
isPassing = false;
} else if ((v.Status.name == ctx.Status.InProgress.name) && (v.id !== issue.id)) {
resultSet = v;
}
});
if (resultSet) {
var otherIssueLink = '<a href="' + resultSet.url + '"> ' + resultSet.id + '</a>';
var message = 'Switch to next open test in current Test Run' + otherIssueLink + '.';
workflow.message(message);
// Updating Test Run Status
parent.fields["Status"] = (isPassing) ? ctx.Status.Passing : ctx.Status.Failing;
} else {
parent.fields["Status"] = (isPassing) ? ctx.Status.Passed : ctx.Status.Failed;
}
}
}
}
}
},
Passed: {
transitions: {
'Failed': {
guard: function(ctx) {
var issue = ctx.issue;
//reconsider condition
return !issue.isChanged('project') && !issue.becomesReported && issue.isReported && (issue.Type.name == ctx.Type.TestExecution.name);
},
targetState: 'Failed',
action: function(ctx) {
var issue = ctx.issue;
if (!issue.links['subtask of'].isEmpty()) {
var parent = issue.links['subtask of'].first();
var TestRunList = parent.links[ctx.Subtask.outward];
var resultSet = null;
TestRunList.forEach(function(v) {
if (v.Status.name == ctx.Status.Failed.name) {
} else if ((v.Status.name == ctx.Status.InProgress.name) && (v.id !== issue.id)) {
resultSet = v;
}
});
if (resultSet) {
var otherIssueLink = '<a href="' + resultSet.url + '"> ' + resultSet.id + '</a>';
var message = 'Switch to next open test in current Test Run' + otherIssueLink + '.';
workflow.message(message);
// Updating Test Run Status
parent.fields["Status"] = ctx.Status.Failing;
} else {
parent.Status = ctx.Status.Failed;
}
}
}
},
'No Run': {
guard: function(ctx) {
var issue = ctx.issue;
//reconsider condition
return !issue.isChanged('project') && !issue.becomesReported && issue.isReported && (issue.Type.name == ctx.Type.TestExecution.name);
},
targetState: 'No Run',
action: function(ctx) {
var issue = ctx.issue;
if (!issue.links['subtask of'].isEmpty()) {
var parent = issue.links['subtask of'].first();
var TestRunList = parent.links[ctx.Subtask.outward];
var ActiveTestRun = false;
var isPassing = true;
TestRunList.forEach(function(v) {
if (v.Status.name == ctx.Status.Failed.name) {
isPassing = false;
ActiveTestRun = true;
} else if ((v.Status.name == ctx.Status.Passed.name) && (v.id !== issue.id)) {
ActiveTestRun = true;
}
});
// Updating Test Run Status
if (ActiveTestRun) {
parent.fields["Status"] = (isPassing) ? ctx.Status.Passing : ctx.Status.Failing;
} else parent.fields["Status"] = ctx.Status.InProgress;
}
}
}
}
},
Failed: {
transitions: {
'Passed': {
guard: function(ctx) {
var issue = ctx.issue;
//reconsider conditions
return !issue.isChanged('project') && !issue.becomesReported && issue.isReported && (issue.Type.name == ctx.Type.TestExecution.name);
},
targetState: 'Passed',
action: function(ctx) {
var issue = ctx.issue;
if (!issue.links['subtask of'].isEmpty()) {
var parent = issue.links['subtask of'].first();
var TestRunList = parent.links[ctx.Subtask.outward];
var resultSet = null;
var isPassing = true;
TestRunList.forEach(function(v) {
if ((v.Status.name == ctx.Status.Failed.name) && (v.id !== issue.id)) {
isPassing = false;
} else if ((v.Status.name == ctx.Status.InProgress.name) && (v.id !== issue.id)) {
resultSet = v;
}
});
if (resultSet) {
var otherIssueLink = '<a href="' + resultSet.url + '"> ' + resultSet.id + '</a>';
var message = 'Switch to next open test in current Test Run' + otherIssueLink + '.';
workflow.message(message);
// Updating Test Run Status
parent.fields["Status"] = (isPassing) ? ctx.Status.Passing : ctx.Status.Failing;
} else {
parent.fields["Status"] = (isPassing) ? ctx.Status.Passed : ctx.Status.Failed;
}
}
}
},
'No Run': {
guard: function(ctx) {
var issue = ctx.issue;
//reconsider condition
return !issue.isChanged('project') && !issue.becomesReported && issue.isReported && (issue.Type.name == ctx.Type.TestExecution.name);
},
targetState: 'No Run',
action: function(ctx) {
var issue = ctx.issue;
if (!issue.links['subtask of'].isEmpty()) {
var parent = issue.links['subtask of'].first();
var TestRunList = parent.links[ctx.Subtask.outward];
var ActiveTestRun = false;
var isPassing = true;
TestRunList.forEach(function(v) {
if ((v.Status.name == ctx.Status.Failed.name) && (v.id !== issue.id)) {
isPassing = false;
ActiveTestRun = true;
} else if ((v.Status.name == ctx.Status.Passed.name) && (v.id !== issue.id)) {
ActiveTestRun = true;
}
});
// Updating Test Run Status
if (ActiveTestRun) {
parent.fields["Status"] = (isPassing) ? ctx.Status.Passing : ctx.Status.Failing;
} else parent.fields["Status"] = ctx.Status.InProgress;
}
}
}
}
}
},
alternativeMachines: {
'Test Run': {
'No Run': {
initial: true,
transitions: {
'Failing': {
targetState: 'Failing',
action: function(ctx) {
workflow.check(false, workflow.i18n('Test Run has read-only status which is defined based on assigned tests statuses'));
}
},
'Failed': {
targetState: 'Failed',
action: function(ctx) {
workflow.check(false, workflow.i18n('Test Run has read-only status which is defined based on assigned tests statuses'));
}
},
'Passing': {
targetState: 'Passing',
action: function(ctx) {
workflow.check(false, workflow.i18n('Test Run has read-only status which is defined based on assigned tests statuses'));
}
},
'Passed': {
targetState: 'Passed',
action: function(ctx) {
workflow.check(false, workflow.i18n('Test Run has read-only status which is defined based on assigned tests statuses'));
}
}
}
},
// Failing state
Failing: {
transitions: {
'Passing': {
targetState: 'Passing',
action: function(ctx) {
workflow.check(false, workflow.i18n('Test Run has read-only status which is defined based on assigned tests statuses'));
}
},
'Passed': {
targetState: 'Passed',
action: function(ctx) {
workflow.check(false, workflow.i18n('Test Run has read-only status which is defined based on assigned tests statuses'));
}
},
'Failed': {
targetState: 'Failed',
action: function(ctx) {
workflow.check(false, workflow.i18n('Test Run has read-only status which is defined based on assigned tests statuses'));
}
}
}
},
//PAssing state
Passing: {
transitions: {
'Failing': {
targetState: 'Passing',
action: function(ctx) {
workflow.check(false, workflow.i18n('Test Run has-read-only status which is defined based on assigned tests statuses'));
}
},
'Passed': {
targetState: 'Passed',
action: function(ctx) {
workflow.check(false, workflow.i18n('Test Run has read-only status which is defined based on assigned tests statuses'));
}
},
'Failed': {
targetState: 'Failed',
action: function(ctx) {
workflow.check(false, workflow.i18n('Test Run has read-only status which is defined based on assigned tests statuses'));
}
}
}
},
//Failed state
Failed: {
transitions: {
'Passing': {
targetState: 'Passing',
action: function(ctx) {
workflow.check(false, workflow.i18n('Test Run has read-only status which is defined based on assigned tests statuses'));
}
},
'Passed': {
targetState: 'Passed',
action: function(ctx) {
workflow.check(false, workflow.i18n('Test Run has read-only status which is defined based on assigned tests statuses'));
}
},
'Failing': {
targetState: 'Failed',
action: function(ctx) {
workflow.check(false, workflow.i18n('Test Run has read-only status which is defined based on assigned tests statuses'));
}
}
}
},
// Passed state
Passed: {
transitions: {
'Passing': {
targetState: 'Passing',
action: function(ctx) {
workflow.check(false, workflow.i18n('Test Run has read-only status which is defined based on assigned tests statuses'));
}
},
'Failed': {
targetState: 'Passed',
action: function(ctx) {
workflow.check(false, workflow.i18n('Test Run has read-only status which is defined based on assigned tests statuses'));
},
},
'Failing': {
targetState: 'Failed',
action: function(ctx) {
workflow.check(false, workflow.i18n('Test Run has read-only status which is defined based on assigned tests statuses'));
}
}
}
}
}
},
requirements: {
Assignee: {
type: entities.User.fieldType
},
Status: {
type: entities.EnumField.fieldType,
InProgress: {
name: 'No Run'
},
Failing: {
name: 'Failing'
},
Passing: {
name: 'Passing'
},
Passed: {
name: 'Passed'
},
Failed: {
name: 'Failed'
},
},
Type: {
type: entities.EnumField.fieldType,
TestRun: {
name: "Test Run"
},
TestExecution: {
name: "Test Case Execution"
}
},
Subtask: {
type: entities.IssueLinkPrototype,
name: 'Subtask',
inward: 'subtask of',
outward: 'parent for'
},
}
});