Setting up test case management workflows in YouTrack
📒

Setting up test case management workflows in YouTrack

Tags
Software Development
YouTrack
JavaScript
Task management
Published
September 4, 2023
Author
plazmius.dev
We are using YouTrack as our task tracker and knowledge base and we were interested in using also for test cases management. JetBrains blog has a nice article explaining how to setup the flow and workflows code that you can import in your instance.
However, we noticed that when you create a Test Run, and attach Test Suite (with a number of Test Cases), Test Case Executing is created for Test Suite. We expected the system to create a Test Case Execution for each Test Case in the Test Suite. For example
notion image
To achieve this, we slightly modified populate-test-run.js to look like this
/** * This is a template for an on-change rule. This rule defines what * happens when a change is applied to an issue. * * 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'); function createExecutionIssues(TestCase, ctx, testRun) { var TestCaseRun = TestCase.copy(); TestCaseRun.Type = ctx.Type.TestExecution.name; TestCaseRun.Status = ctx.Status.NoRun.name; // Remove all links from Test Case Execution Object.keys(TestCaseRun.links).forEach(function (linkType) { if (!TestCaseRun.links[linkType]) return; TestCaseRun.links[linkType].clear(); }); TestCaseRun.summary = "[TEST_CASE_EXECUTION" + "] [" + TestCaseRun.summary + "]"; // Links population TestCaseRun.links[ctx.Subtask.inward].add(testRun); testRun.links[ctx.Subtask.outward].add(TestCaseRun); TestCaseRun.links[ctx.Execution.outward].add(TestCase); } function createRecursive(TestSuite, ctx, parent) { TestSuite.links[ctx.Subtask.outward].forEach(childSuite => { if (childSuite.Type.name !== ctx.Type.TestSuite.name) return; createRecursive(childSuite, ctx, parent); }); // New issue creation TestSuite.links[ctx.Subtask.outward].forEach(testCase => { if (testCase.Type.name !== ctx.Type.TestCase.name) return; createExecutionIssues(testCase, ctx, parent); }); } exports.rule = entities.Issue.onChange({ title: 'Populate-test-run', guard: function (ctx) { var issue = ctx.issue; return !issue.isChanged('project') && issue.Type && (issue.Type.name == ctx.Type.TestRun.name) && issue.links[ctx.Execution.outward].added.isNotEmpty() && issue.isReported; }, action: function (ctx) { var testRun = ctx.issue; var totalTestRuns = testRun.links[ctx.Execution.outward].added.size; testRun.links[ctx.Execution.outward].added.forEach(function (TestCase) { var message = '<a href="' + TestCase.url + '"> ' + TestCase.id + '</a>'; workflow.check((TestCase.Type.name === ctx.Type.TestCase.name) || (TestCase.Type.name === ctx.Type.TestSuite.name), workflow.i18n('\'Test Run\' can be linked to \'Test Case\' and \'Test Suite\' only, but {0} has \'{1}\' type!', message, TestCase.Type.name)); TestCase.links[ctx.Execution.inward].delete(testRun); if (TestCase.Type.name === ctx.Type.TestSuite.name) { createRecursive(TestCase, ctx, testRun); return; } createExecutionIssues(TestCase, ctx, testRun); }); testRun.fields['Total number of test cases'] = totalTestRuns; }, requirements: { Execution: { type: entities.IssueLinkPrototype, name: 'Execution', inward: 'Execution', outward: 'Assigned test case or test suite' }, Subtask: { type: entities.IssueLinkPrototype, name: 'Subtask', inward: 'parent for', outward: 'subtask of' }, Type: { type: entities.EnumField.fieldType, TestExecution: { name: "Test Case Execution" }, TestRun: { name: "Test Run" }, TestCase: { name: "Test Case" }, TestSuite: { name: "Test Suite" } }, Total: { type: entities.Field.integerType, name: 'Total number of test cases' }, TotalFailed: { type: entities.Field.integerType, name: 'Number of failed test cases' }, TotalPassed: { type: entities.Field.integerType, name: 'Number of passed test cases' }, Status: { type: entities.EnumField.fieldType, InProgress: { name: 'In Progress' }, Passed: { name: 'Passed' }, Failed: { name: 'Failed' }, NoRun: { name: 'No Run' }, }, } });
The important bits are createExecutionIssues and createRecursive functions, which create executions for all test cases recursively.