{"id":98667,"date":"2020-11-27T18:10:08","date_gmt":"2020-11-27T17:10:08","guid":{"rendered":"https:\/\/blog.jetbrains.com\/?post_type=youtrack&#038;p=98667"},"modified":"2020-11-27T18:10:08","modified_gmt":"2020-11-27T17:10:08","slug":"make-it-workflow-parte-13-compatibilidad-con-escenarios-de-gestion-de-pruebas","status":"publish","type":"youtrack","link":"https:\/\/blog.jetbrains.com\/es\/youtrack\/2020\/11\/make-it-workflow-parte-13-compatibilidad-con-escenarios-de-gestion-de-pruebas\/","title":{"rendered":"Make It Workflow \u2014 Parte 13: Compatibilidad con escenarios de gesti\u00f3n de pruebas"},"content":{"rendered":"<p><a href=\"https:\/\/blog.jetbrains.com\/youtrack\/2020\/10\/make-it-workflow-part-13-supporting-test-management-scenarios\/\"><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/09\/YT_workflow_en-1.png\" alt=\"\" \/><\/a><\/p>\n<p>\u00a1Bienvenidos de nuevo a nuestra serie Make It Workflow! Hemos recibido varias peticiones de la comunidad para que hablemos de las funcionalidades de YouTrack que puede utilizar el equipo de QA (Quality Assurance). Hoy nos gustar\u00eda echar un vistazo a c\u00f3mo efectuar el mantenimiento de los escenarios de gesti\u00f3n de pruebas (TMS, test management scenarios) en YouTrack. Este art\u00edculo le mostrar\u00e1 c\u00f3mo configurar YouTrack para que pueda mantener la funcionalidad de gesti\u00f3n de pruebas dentro del propio YouTrack, sin tener que utilizar soluciones de terceros. Esto le ayuda a ahorrar en costes de licencias y hace que el proceso de gesti\u00f3n de pruebas fluya mejor.<\/p>\n<p>Este art\u00edculo est\u00e1 destinado a gestores de pruebas y a cualquiera interesado en flujos de trabajo de YouTrack. Encontrar\u00e1 un ejemplo pr\u00e1ctico de c\u00f3mo crear un proceso de pruebas en YouTrack, con recomendaciones de ajustes para proyectos e incidencias, una descripci\u00f3n de c\u00f3mo implementar escenarios de gesti\u00f3n de pruebas, y mucho m\u00e1s.<\/p>\n<p><span id=\"more-77371\"><\/span><\/p>\n<p>Tambi\u00e9n hemos preparado una lista de bloques de c\u00f3digo de flujos de trabajo listos para usar, que puede emplear para automatizar procesos de gesti\u00f3n de pruebas. Estos bloques de c\u00f3digo facilitan la asociaci\u00f3n de casos de prueba con la operaci\u00f3n de pruebas espec\u00edfica, clonaci\u00f3n de operaci\u00f3n de pruebas, mostrar sugerencias del sistema en la prueba siguiente, y m\u00e1s.<br \/>\n<a name=\"ref1\"><\/a><\/p>\n<h2>Ajustes para proyectos de gesti\u00f3n de pruebas en YouTrack<\/h2>\n<p>Quiz\u00e1 desee utilizar tipos de incidencias espec\u00edficos para la gesti\u00f3n de pruebas en<br \/>\n sus proyectos de gesti\u00f3n de pruebas, como Test Case (caso de pruebas), Test Suite (un grupo de casos de pruebas), Test Run (un conjunto de casos de pruebas o de grupos de casos asignados a un ciclo de pruebas espec\u00edfico) y Test Case Executions (ejecuciones de casos de pruebas) asignados a una Test Run (operaci\u00f3n de pruebas) espec\u00edfica. Tendr\u00e1 que <a href=\"https:\/\/www.jetbrains.com\/help\/youtrack\/standalone\/Manage-Custom-Fields-Per-Project.html#add-custom-field-to-project\" target=\"_blank\" rel=\"noopener\"> configurar<\/a> los tipos de incidencias necesarios para su proyecto TMS. Hay 3 tipos de <a href=\"https:\/\/www.jetbrains.com\/help\/youtrack\/standalone\/Link-Types.html\" target=\"_blank\" rel=\"noopener\"> enlaces a incidencias<\/a> para establecer conexiones y relevancia entre sus tareas de gesti\u00f3n de pruebas:<\/p>\n<ul>\n<li>Las &#8220;parent-subtasks&#8221; (o subtareas primarias) est\u00e1ndar mantienen operaciones de pruebas y ejecuciones de casos de pruebas, as\u00ed como grupos de pruebas y relaciones de casos de pruebas. <\/li>\n<li>La &#8220;Execution&#8221; (ejecuci\u00f3n) personalizada mantiene las relaciones de ejecuci\u00f3n de casos de pruebas y los casos de pruebas. <\/li>\n<li>El &#8220;Related Bug&#8221; (error relacionado) personalizado mantiene las relaciones entre errores asignados y pruebas fallidas.<\/li>\n<\/ul>\n<p>Los requisitos de prueba se pueden mantener como <a href=\"https:\/\/www.jetbrains.com\/help\/youtrack\/standalone\/create-articles.html\" target=\"_blank\" rel=\"noopener\">art\u00edculo<\/a> vinculado a las incidencias de un campo de texto.<br \/>\nAdem\u00e1s, para sus tareas de gesti\u00f3n de pruebas, quiz\u00e1 desee configurar <a href=\"https:\/\/www.jetbrains.com\/help\/youtrack\/standalone\/Manage-Custom-Fields-Per-Project.html#add-custom-field-to-project\" target=\"_blank\" rel=\"noopener\">campos personalizados<\/a> como <em>Modo de prueba<\/em>, <em>Categor\u00eda<\/em>, <em>Flujo de prueba<\/em> y <em>Aplicaci\u00f3n<\/em>, junto con valores predefinidos (p. ej. estados de pruebas predefinidos). Tambi\u00e9n puede utilizar <a href=\"https:\/\/www.jetbrains.com\/help\/youtrack\/standalone\/conditional-custom-fields.html\" target=\"_blank\" rel=\"noopener\">campos condicionales<\/a> cuando tiene que mostrar informaci\u00f3n espec\u00edficamente para un tipo de incidencia.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/09\/TMS0_1.png\" alt=\"\" \/><\/p>\n<p>Puede ajustarse un conjunto de campos predefinidos para los tipos de incidencia &#8220;Test Case&#8221; y &#8220;Test Run&#8221; seg\u00fan las necesidades de su empresa.<\/p>\n<h2>Configuraci\u00f3n de operaciones de pruebas y ejecuci\u00f3n de pruebas<\/h2>\n<p>Cuando, por ejemplo, deseamos probar una versi\u00f3n espec\u00edfica de un producto, tenemos que crear una operaci\u00f3n de pruebas y asignarle un n\u00famero de grupos de pruebas y casos de pruebas relevantes. Estos son los pasos a seguir para hacerlo:<\/p>\n<ul>\n<li>Cree una incidencia con el tipo de incidencia &#8220;Test Run&#8221;. <\/li>\n<li>Use the \u2018Assigned test case and test suite\u2019 custom link type to link to the issue.<\/li>\n<li>En la ventana emergente, especifique los casos de pruebas que le gustar\u00eda incluir en la operaci\u00f3n de pruebas.<\/li>\n<\/ul>\n<p>Los pasos del flujo de trabajo \u2018&#8221;Populate Test Run&#8221; le ahorrar\u00e1n tiempo y esfuerzo a su equipo de QA:<\/p>\n<ul>\n<li>Se crear\u00e1n en el sistema copias de todos los grupos de pruebas y casos de pruebas seleccionados. Todas las incidencias ser\u00e1n del tipo &#8220;Test Case Execution&#8221;. <\/li>\n<li>El tipo de enlace &#8220;Execution&#8221; conectar\u00e1 ejecuciones de casos de pruebas con casos de pruebas seleccionados.<\/li>\n<li>Las incidencias nuevas que se creen se vincular\u00e1n con la operaci\u00f3n de prueba como incidencia primaria y sus subtareas correspondientes.<\/li>\n<\/ul>\n<p>As\u00ed funciona:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/resources.jetbrains.com\/storage\/products\/blog\/wp-content\/uploads\/done1s720cut.gif\" alt=\"\" \/><\/p>\n<details>\n<summary>\n        Ampliar bloque de c\u00f3digo<br \/>\n    <\/summary>\n<p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-title=\"\">\nvar entities = require(&#039;@jetbrains\/youtrack-scripting-api\/entities&#039;);\nvar workflow = require(&#039;@jetbrains\/youtrack-scripting-api\/workflow&#039;);\n\nexports.rule = entities.Issue.onChange({\n  title: &#039;Populate-test-run&#039;,\n  guard: function(ctx) {\n    var issue = ctx.issue;\n    return !issue.isChanged(&#039;project&#039;) &amp;&amp; issue.Type &amp;&amp; (issue.Type.name == ctx.Type.TestRun.name) &amp;&amp; issue.links[ctx.Execution.outward].added.isNotEmpty() &amp;&amp; issue.isReported;\n  },\n  action: function(ctx) {\n    var issue = ctx.issue;\n    var totalTestRuns = issue.links[ctx.Execution.outward].added.size;\n    issue.links[ctx.Execution.outward].added.forEach(function(TestCase) {\n      TestCase.links[ctx.Execution.inward].delete(issue);\n\n      var TestCaseRun = TestCase.copy();\n      TestCaseRun.Type = ctx.Type.TestExecution.name;\n      TestCaseRun.Status = ctx.Status.NoRun.name;\n      Object.keys(TestCaseRun.links).forEach(function(linkType) {\n       if (!TestCaseRun.links[linkType])\n        return;\n         TestCaseRun.links[linkType].clear();\n      });\n      TestCaseRun.summary = &quot;[TEST_CASE_EXECUTION&quot; + &quot;] [&quot; + TestCaseRun.summary + &quot;]&quot;;\n\n      TestCaseRun.links[ctx.Subtask.inward].add(issue);\n      issue.links[ctx.Subtask.outward].add(TestCaseRun);\n      TestCaseRun.links[ctx.Execution.outward].add(TestCase);\n    });\n    issue.fields[&#039;Total number of test cases&#039;] = totalTestRuns;\n  },\n  requirements: {\n    Execution: {\n      type: entities.IssueLinkPrototype,\n      name: &#039;Execution&#039;,\n      inward: &#039;Execution&#039;,\n      outward: &#039;Assigned test case or test suite&#039;\n    },\n    Subtask: {\n      type: entities.IssueLinkPrototype,\n      name: &#039;Subtask&#039;,\n      inward: &#039;parent for&#039;,\n      outward: &#039;subtask of&#039;\n    },\n    Type: {\n      type: entities.EnumField.fieldType,\n      TestExecution: {\n        name: &quot;Test Case Execution&quot;\n      },\n      TestRun: {\n        name: &quot;Test Run&quot;\n      },\n      TestCase: {\n        name: &quot;Test Case&quot;\n      },\n      TestSuite: {\n        name: &quot;Test Suite&quot;\n      }\n    },\n    Total: {\n      type: entities.Field.integerType,\n      name: &#039;Total number of test cases&#039;\n    },\n    TotalFailed: {\n      type: entities.Field.integerType,\n      name: &#039;Number of failed test cases&#039;\n    },\n    TotalPassed: {\n      type: entities.Field.integerType,\n      name: &#039;Number of passed test cases&#039;\n    },\n    Status: {\n      type: entities.EnumField.fieldType,\n      InProgress: {\n        name: &#039;In Progress&#039;\n      },\n      Passed: {\n        name: &#039;Passed&#039;\n      },\n      Failed: {\n        name: &#039;Failed&#039;\n      },\n      NoRun: {\n        name: &#039;No Run&#039;\n      },\n    },\n  }\n});\n<\/pre>\n<p>\n<\/details>\n<h2>Mantenimiento de actividades de prueba<\/h2>\n<p>Cuando las operaciones de pruebas est\u00e9n totalmente configuradas, un ingeniero de pruebas puede comenzar a realizarlas. De forma predeterminada, todas las incidencias de tipo &#8220;Test Case Execution&#8221; y &#8220;Test Run&#8221; se encuentran en estado &#8220;No Run&#8221;.<\/p>\n<h3>C\u00f3mo cambiar entre pruebas<\/h3>\n<p>Cuando trabaja con una serie de pruebas, tiene dos modos de cambiar de una prueba a otra:<\/p>\n<ol>\n<li>Cambie manualmente el estado de la prueba en la p\u00e1gina de la lista de incidencias. <\/li>\n<li>Abra una prueba y cambie a la siguiente prueba no completada. When you want to switch tests in this way, a helpful pop-up appears that suggests the next test to run.<br \/>\n    The following actions can be automated:<\/p>\n<ul>\n<li> Compruebe si hay pruebas en estado &#8220;No Run&#8221; y pertenecen a la misma operaci\u00f3n de pruebas.<\/li>\n<li> Muestre un mensaje con la URL de la siguiente prueba disponible (si hay alguna).\n<p>    <a href=\"https:\/\/resources.jetbrains.com\/storage\/products\/blog\/wp-content\/uploads\/done3s720.gif\" rel=\"attachment wp-att-6366 noopener\" target=\"_blank\"><img decoding=\"async\" src=\"https:\/\/resources.jetbrains.com\/storage\/products\/blog\/wp-content\/uploads\/done3s720.gif\" alt=\"ApplyingCommandForSubsystem\"  \/><\/a><\/li>\n<\/ul>\n<p> Esto puede realizarse utilizando el c\u00f3digo siguiente.<\/p>\n<details>\n<summary>\n        Ampliar bloque de c\u00f3digo<br \/>\n    <\/summary>\n<p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-title=\"\">\n action: function(ctx) {\n  var issue = ctx.issue;\n  if (!issue.links[&#039;subtask of&#039;].isEmpty()) {\n              var parent = issue.links[&#039;subtask of&#039;].first();\n              var TestRunList = parent.links[ctx.Subtask.outward];\n              var resultSet = null;\n              var isPassing = true;\n              TestRunList.forEach(function(v) {\n                if (v.Status.name == ctx.Status.Failed.name) {\n                  isPassing = false;\n                } else if ((v.Status.name == ctx.Status.InProgress.name) &amp;&amp; (v.id !== issue.id)) {\n                  resultSet = v;\n                }\n              });\n              if (resultSet) {\n                var otherIssueLink = &#039;&lt;a href=&quot;&#039; + resultSet.url + &#039;&quot;&gt; &#039; + resultSet.id + &#039;&lt;\/a&gt;&#039;;\n                var message = &#039;Switch to next open test in current Test Run&#039; + otherIssueLink + &#039;.&#039;;\n                workflow.message(message);\n    }\n}\n}\n<\/pre>\n<p>\n<\/details>\n<\/ol>\n<h2>Estados de las pruebas <\/h2>\n<p><a name=\"ref2\"><\/a><\/p>\n<p>Al cambiar de una prueba a otra, un ingeniero de pruebas puede indicar que la prueba ha quedado &#8220;Passed&#8221; (Superada) o cambiar el estado de la prueba a &#8220;Failed&#8221; (Fallida) (en el caso de que se haya identificado un error durante la ejecuci\u00f3n del caso de prueba). A partir de los <a href=\"#ref1\">ajustes de YouTrack<\/a> que hemos configurado inicialmente para este proyecto, hay varios estados de operaciones de pruebas predefinidos:<\/p>\n<ul>\n<li>Failing: al menos una de las pruebas asociadas se encuentra en estado &#8220;No Run&#8221; y al menos una de las pruebas asociadas se encuentra en estado &#8220;Failed&#8221;.<\/li>\n<li>Passing: al menos una de las pruebas asociadas se encuentra en estado &#8220;No Run&#8221; y no hay pruebas asociadas en estado &#8220;Failed&#8221;.<\/li>\n<li>Failed: no hay ninguna prueba asociada en estado &#8220;No Run&#8221; y al menos una de las pruebas asociadas se encuentra en estado &#8220;Failed&#8221;.<\/li>\n<li>Passed: ninguna prueba asociada se encuentra en estado &#8220;No Run&#8221; o &#8220;Failed&#8221;.<\/li>\n<\/ul>\n<p><img decoding=\"async\" src=\"https:\/\/resources.jetbrains.com\/storage\/products\/blog\/wp-content\/uploads\/done2s720.gif\" alt=\"\" \/><\/p>\n<p>Los estados listados anteriormente pueden ayudar a su equipo de QA a monitorizar los progresos de las pruebas a trav\u00e9s de todo un ciclo de pruebas. Es importante tener en cuenta que el estado de la operaci\u00f3n de pruebas depende solo de las pruebas asociadas y no deber\u00eda cambiarse manualmente. El proceso de determinar estados de operaciones de pruebas se implementa utilizando el flujo de trabajo <a href=\"https:\/\/www.jetbrains.com\/help\/youtrack\/standalone\/state-machine-per-issue-type.html#state-machine-per-issue-type2\" target=\"_blank\" rel=\"noopener\"> state-machines per issue type<\/a>. El bloque de c\u00f3digo para cambiar entre pruebas tambi\u00e9n se incorpora en el flujo de trabajo.<\/p>\n<details>\n<summary>\n        Ampliar bloque de c\u00f3digo<br \/>\n    <\/summary>\n<p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-title=\"\">\n\nvar entities = require(&#039;@jetbrains\/youtrack-scripting-api\/entities&#039;);\nvar workflow = require(&#039;@jetbrains\/youtrack-scripting-api\/workflow&#039;);\n\nexports.rule = entities.Issue.stateMachine({\n  title: &#039;status-management&#039;,\n  stateFieldName: &#039;Status&#039;,\n  typeFieldName: &#039;Type&#039;,\n  defaultMachine: {\n    &#039;No Run&#039;: {\n      initial: true,\n      transitions: {\n        &#039;Failed&#039;: {\n          targetState: &#039;Failed&#039;,\n          action: function(ctx) {\n            var issue = ctx.issue;\n            if (!issue.links[&#039;subtask of&#039;].isEmpty()) {\n              var parent = issue.links[&#039;subtask of&#039;].first();\n              var TestRunList = parent.links[ctx.Subtask.outward];\n              var resultSet = null;\n              var isPassing = true;\n              TestRunList.forEach(function(v) {\n                if (v.Status.name == ctx.Status.Failed.name) {\n                  isPassing = false;\n                } else if ((v.Status.name == ctx.Status.InProgress.name) &amp;&amp; (v.id !== issue.id)) {\n                  resultSet = v;\n                }\n              });\n              if (resultSet) {\n                var otherIssueLink = &#039;&lt;a href=&quot;&#039; + resultSet.url + &#039;&quot;&gt; &#039; + resultSet.id + &#039;&lt;\/a&gt;&#039;;\n                var message = &#039;Switch to next open test in current Test Run&#039; + otherIssueLink + &#039;.&#039;;\n                workflow.message(message);\n                \/\/ Updating Test Run Status \n                parent.fields[&quot;Status&quot;] = ctx.Status.Failing;\n              } else {\n                parent.fields[&quot;Status&quot;] = ctx.Status.Failed;\n              }\n            }\n          }\n        },\n        &#039;Passed&#039;: {\n          guard: function(ctx) {\n            var issue = ctx.issue;\n            return !issue.isChanged(&#039;project&#039;) &amp;&amp; !issue.becomesReported &amp;&amp; issue.isReported &amp;&amp; (issue.Type.name == ctx.Type.TestExecution.name);\n          },\n          targetState: &#039;Passed&#039;,\n          action: function(ctx) {\n            var issue = ctx.issue;\n            if (!issue.links[&#039;subtask of&#039;].isEmpty()) {\n              var parent = issue.links[&#039;subtask of&#039;].first();\n              var TestRunList = parent.links[ctx.Subtask.outward];\n              var resultSet = null;\n              var isPassing = true;\n              TestRunList.forEach(function(v) {\n                if (v.Status.name == ctx.Status.Failed.name) {\n                  isPassing = false;\n                } else if ((v.Status.name == ctx.Status.InProgress.name) &amp;&amp; (v.id !== issue.id)) {\n                  resultSet = v;\n                }\n              });\n              if (resultSet) {\n                var otherIssueLink = &#039;&lt;a href=&quot;&#039; + resultSet.url + &#039;&quot;&gt; &#039; + resultSet.id + &#039;&lt;\/a&gt;&#039;;\n                var message = &#039;Switch to next open test in current Test Run&#039; + otherIssueLink + &#039;.&#039;;\n                workflow.message(message);\n                parent.fields[&quot;Status&quot;] = (isPassing) ? ctx.Status.Passing : ctx.Status.Failing;\n              } else {\n                parent.fields[&quot;Status&quot;] = (isPassing) ? ctx.Status.Passed : ctx.Status.Failed;\n              }\n            }\n          }\n        }\n      }\n    },\n    Passed: {\n      transitions: {\n        &#039;Failed&#039;: {\n          guard: function(ctx) {\n            var issue = ctx.issue;\n            return !issue.isChanged(&#039;project&#039;) &amp;&amp; !issue.becomesReported &amp;&amp; issue.isReported &amp;&amp; (issue.Type.name == ctx.Type.TestExecution.name);\n          },\n          targetState: &#039;Failed&#039;,\n          action: function(ctx) {\n            var issue = ctx.issue;\n            if (!issue.links[&#039;subtask of&#039;].isEmpty()) {\n             var parent = issue.links[&#039;subtask of&#039;].first();\n              var TestRunList = parent.links[ctx.Subtask.outward];\n              var resultSet = null;\n              TestRunList.forEach(function(v) {\n                if (v.Status.name == ctx.Status.Failed.name) {\n                } else if ((v.Status.name == ctx.Status.InProgress.name) &amp;&amp; (v.id !== issue.id)) {\n                  resultSet = v;\n                }\n              });\n              if (resultSet) {\n                var otherIssueLink = &#039;&lt;a href=&quot;&#039; + resultSet.url + &#039;&quot;&gt; &#039; + resultSet.id + &#039;&lt;\/a&gt;&#039;;\n                var message = &#039;Switch to next open test in current Test Run&#039; + otherIssueLink + &#039;.&#039;;\n                workflow.message(message);\n                parent.fields[&quot;Status&quot;] = ctx.Status.Failing;\n              } else {\n                parent.Status = ctx.Status.Failed;\n              }\n            }\n          }\n        },\n        &#039;No Run&#039;: {\n          guard: function(ctx) {\n            var issue = ctx.issue;\n            return !issue.isChanged(&#039;project&#039;) &amp;&amp; !issue.becomesReported &amp;&amp; issue.isReported &amp;&amp; (issue.Type.name == ctx.Type.TestExecution.name);\n          },\n          targetState: &#039;No Run&#039;,\n          action: function(ctx) {\n            var issue = ctx.issue;\n            if (!issue.links[&#039;subtask of&#039;].isEmpty()) {\n              var parent = issue.links[&#039;subtask of&#039;].first();\n              var TestRunList = parent.links[ctx.Subtask.outward];\n              var ActiveTestRun = false;\n              var isPassing = true;\n              TestRunList.forEach(function(v) {\n                if (v.Status.name == ctx.Status.Failed.name) {\n                  isPassing = false;\n                  ActiveTestRun = true;\n                } else if ((v.Status.name == ctx.Status.Passed.name) &amp;&amp; (v.id !== issue.id)) {\n                  ActiveTestRun = true;\n                }\n              });\n              if (ActiveTestRun) {\n                parent.fields[&quot;Status&quot;] = (isPassing) ? ctx.Status.Passing : ctx.Status.Failing;\n              } else parent.fields[&quot;Status&quot;] = ctx.Status.InProgress;\n            }\n          }\n        }\n      }\n    },\n    Failed: {\n      transitions: {\n        &#039;Passed&#039;: {\n          guard: function(ctx) {\n            var issue = ctx.issue;\n            return !issue.isChanged(&#039;project&#039;) &amp;&amp; !issue.becomesReported &amp;&amp; issue.isReported &amp;&amp; (issue.Type.name == ctx.Type.TestExecution.name);\n          },\n          targetState: &#039;Passed&#039;,\n          action: function(ctx) {\n            var issue = ctx.issue;\n            if (!issue.links[&#039;subtask of&#039;].isEmpty()) {\n              var parent = issue.links[&#039;subtask of&#039;].first();\n              var TestRunList = parent.links[ctx.Subtask.outward];\n              var resultSet = null;\n              var isPassing = true;\n              TestRunList.forEach(function(v) {\n                if ((v.Status.name == ctx.Status.Failed.name) &amp;&amp; (v.id !== issue.id)) {\n                  isPassing = false;\n                } else if ((v.Status.name == ctx.Status.InProgress.name) &amp;&amp; (v.id !== issue.id)) {\n                  resultSet = v;\n                }\n              });\n              if (resultSet) {\n                var otherIssueLink = &#039;&lt;a href=&quot;&#039; + resultSet.url + &#039;&quot;&gt; &#039; + resultSet.id + &#039;&lt;\/a&gt;&#039;;\n                var message = &#039;Switch to next open test in current Test Run&#039; + otherIssueLink + &#039;.&#039;;\n                workflow.message(message);\n\n                parent.fields[&quot;Status&quot;] = (isPassing) ? ctx.Status.Passing : ctx.Status.Failing;\n              } else {\n                parent.fields[&quot;Status&quot;] = (isPassing) ? ctx.Status.Passed : ctx.Status.Failed;\n              }\n            }\n          }\n        },\n        &#039;No Run&#039;: {\n          guard: function(ctx) {\n            var issue = ctx.issue;\n            return !issue.isChanged(&#039;project&#039;) &amp;&amp; !issue.becomesReported &amp;&amp; issue.isReported &amp;&amp; (issue.Type.name == ctx.Type.TestExecution.name);\n          },\n          targetState: &#039;No Run&#039;,\n          action: function(ctx) {\n            var issue = ctx.issue;\n            if (!issue.links[&#039;subtask of&#039;].isEmpty()) {\n              var parent = issue.links[&#039;subtask of&#039;].first();\n              var TestRunList = parent.links[ctx.Subtask.outward];\n              var ActiveTestRun = false;\n              var isPassing = true;\n              TestRunList.forEach(function(v) {\n                if ((v.Status.name == ctx.Status.Failed.name) &amp;&amp; (v.id !== issue.id)) {\n                  isPassing = false;\n                  ActiveTestRun = true;\n                } else if ((v.Status.name == ctx.Status.Passed.name) &amp;&amp; (v.id !== issue.id)) {\n                  ActiveTestRun = true;\n                }\n              });\n              if (ActiveTestRun) {\n                parent.fields[&quot;Status&quot;] = (isPassing) ? ctx.Status.Passing : ctx.Status.Failing;\n              } else parent.fields[&quot;Status&quot;] = ctx.Status.InProgress;\n            }\n          }\n        }\n      }\n    }\n  },\n  alternativeMachines: {\n    &#039;Test Run&#039;: {\n      &#039;No Run&#039;: {\n        initial: true,\n        transitions: {\n          &#039;Failing&#039;: {\n            targetState: &#039;Failing&#039;,\n            action: function(ctx) {\n           \/* Add actions. *\/\n            }\n          },\n          &#039;Failed&#039;: {\n            targetState: &#039;Failed&#039;,\n            action: function(ctx) {\n         \/* Add actions. *\/\n            }\n          },\n          &#039;Passing&#039;: {\n            targetState: &#039;Passing&#039;,\n            action: function(ctx) {\n          \/* Add actions. *\/\n            }\n          },\n          &#039;Passed&#039;: {\n            targetState: &#039;Passed&#039;,\n            action: function(ctx) {\n           \/* Add actions. *\/\n            }\n          }\n        }\n      },\n      Failing: {\n        transitions: {\n          &#039;Passing&#039;: {\n            targetState: &#039;Passing&#039;,\n            action: function(ctx) {\n           \/* Add actions . *\/\n            }\n          },\n          &#039;Passed&#039;: {\n            targetState: &#039;Passed&#039;,\n            action: function(ctx) {\n          \/* Add actions. *\/\n            }\n          },\n          &#039;Failed&#039;: {\n            targetState: &#039;Failed&#039;,\n            action: function(ctx) {\n           \/* Add actions. *\/\n            }\n          }\n        }\n      },\n      Passing: {\n        transitions: {\n          &#039;Failing&#039;: {\n            targetState: &#039;Passing&#039;,\n            action: function(ctx) {\n              workflow.check(false, workflow.i18n(&#039;Test Run has-read-only status which is defined based on assigned tests statuses&#039;));\n            }\n          },\n          &#039;Passed&#039;: {\n            targetState: &#039;Passed&#039;,\n            action: function(ctx) {\n           \/* Add actions. *\/\n            }\n          },\n          &#039;Failed&#039;: {\n            targetState: &#039;Failed&#039;,\n            action: function(ctx) {\n           \/* Add actions. *\/\n            }\n          }\n        }\n      },\n      Failed: {\n        transitions: {\n          &#039;Passing&#039;: {\n            targetState: &#039;Passing&#039;,\n            action: function(ctx) {\n           \/* Add actions. *\/\n            }\n          },\n          &#039;Passed&#039;: {\n            targetState: &#039;Passed&#039;,\n            action: function(ctx) {\n           \/* Add actions. *\/\n            }\n          },\n          &#039;Failing&#039;: {\n            targetState: &#039;Failed&#039;,\n            action: function(ctx) {\n           \/* Add actions. *\/\n            }\n          }\n        }\n      },\n      Passed: {\n        transitions: {\n          &#039;Passing&#039;: {\n            targetState: &#039;Passing&#039;,\n            action: function(ctx) {\n           \/* Add actions. *\/\n            }\n          },\n          &#039;Failed&#039;: {\n            targetState: &#039;Passed&#039;,\n            action: function(ctx) {\n          \/* Add actions. *\/\n            },\n          },\n          &#039;Failing&#039;: {\n            targetState: &#039;Failed&#039;,\n            action: function(ctx) {\n           \/* Add actions. *\/\n            }\n          }\n        }\n      }\n    }\n  },\n  requirements: {\n    Assignee: {\n      type: entities.User.fieldType\n    },\n    Status: {\n      type: entities.EnumField.fieldType,\n      InProgress: {\n        name: &#039;No Run&#039;\n      },\n      Failing: {\n        name: &#039;Failing&#039;\n      },\n      Passing: {\n        name: &#039;Passing&#039;\n      },\n      Passed: {\n        name: &#039;Passed&#039;\n      },\n      Failed: {\n        name: &#039;Failed&#039;\n      },\n    },\n    Type: {\n      type: entities.EnumField.fieldType,\n      TestRun: {\n        name: &quot;Test Run&quot;\n      },\n      TestExecution: {\n        name: &quot;Test Case Execution&quot;\n      }\n    },\n    Subtask: {\n      type: entities.IssueLinkPrototype,\n      name: &#039;Subtask&#039;,\n      inward: &#039;subtask of&#039;,\n      outward: &#039;parent for&#039;\n    },\n  }\n});\n <\/pre>\n<p>\n <\/details>\n<h1>Estad\u00edsticas de pruebas<\/h1>\n<p>Puede que tambi\u00e9n le interesen las estad\u00edsticas de ciclos de pruebas clave. Para esta demostraci\u00f3n hemos incorporado las m\u00e9tricas siguientes:<\/p>\n<ul>\n<li>N\u00famero total de pruebas asignadas a una operaci\u00f3n de prueba espec\u00edfica.<\/li>\n<li>N\u00famero de pruebas en estado &#8220;Passed&#8221;.<\/li>\n<li>N\u00famero de pruebas en estado &#8220;Failed&#8221;.<\/li>\n<\/ul>\n<p><img decoding=\"async\" src=\"https:\/\/resources.jetbrains.com\/storage\/products\/blog\/wp-content\/uploads\/done5s720.gif\" alt=\"\" \/><br \/>\nGracias al c\u00f3digo siguiente, todas las m\u00e9tricas se actualizan en caso de cualquiera de los cambios siguientes: cambios en el estado de las pruebas, asignaci\u00f3n de una nueva prueba a la operaci\u00f3n de pruebas, y eliminaci\u00f3n de una prueba de una operaci\u00f3n de pruebas.<\/p>\n<details>\n<summary>\n        Ampliar bloque de c\u00f3digo<br \/>\n    <\/summary>\n<p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-title=\"\">\n  exports.calculateStatuses = function(parent) {\n  var totalTR = 0;\n  var totalFailed = 0;\n  var totalPassed = 0;\n  if (!parent.links[&#039;parent for&#039;]) {\n    return;\n  } else {\n    parent.links[&#039;parent for&#039;].forEach(function(tr) {\n      totalTR++;\n      if (tr.Status.name == &#039;Passed&#039;) {\n        totalFailed++;\n      }\n      if (tr.Status.name == &#039;Failed&#039;) {\n        totalPassed++;\n      }\n    });\n    parent.fields[&#039;Total number of test cases&#039;] = totalTR;\n    parent.fields[&#039;Number of passed test cases&#039;] = totalPassed;\n    parent.fields[&#039;Number of failed test cases&#039;] = totalFailed;\n    return true;\n  }\n};\nexports.resetStatuses = function(testRun, testRunCopy) {\n   testRunCopy.fields[&#039;Total number of test cases&#039;] = testRun.fields[&#039;Total number of test cases&#039;];\n   testRunCopy.fields[&#039;Number of passed test cases&#039;] = 0;\n   testRunCopy.fields[&#039;Number of failed test cases&#039;] = 0;\n  return true;\n};\n<\/pre>\n<p>\n<\/details>\n<p>En nuestro caso, este c\u00f3digo se activa en varias reglas de flujo de trabajo, como &#8220;Update stats when links are adjusted&#8221;, &#8220;Switch to the next test case&#8221;, y otras. Si desea adoptar una serie de m\u00e9tricas que se adapten a sus necesidades espec\u00edficas, puede a\u00f1adir los campos personalizados necesarios y ajustar la l\u00f3gica de su flujo de trabajo.<\/p>\n<h2>Funcionalidades de la herramienta de seguimiento de incidencias<\/h2>\n<p>Quiz\u00e1 desee crear una tarea por separado que trate un error encontrado en una prueba fallida y vincularla a una incidencia que identifique la ejecuci\u00f3n de caso de pruebas correspondiente. Puede vincular pruebas fallidas con sus errores correspondientes utilizando un tipo de enlace de incidencia personalizado, o utilizar un campo de texto personalizado que contenga una referencia al error. Podr\u00eda resultar m\u00e1s f\u00e1cil organizar un proyecto de YouTrack por separado para efectuar un seguimiento de los errores.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/09\/TMS7.png\" alt=\"\" \/><\/p>\n<h2>Posibles mejoras<\/h2>\n<p>Puede ampliar las funciones de YouTrack para cumplir requisitos adicionales de la empresa. Estas son varias opciones que podr\u00eda tener en cuenta.<\/p>\n<h3>Clonar una operaci\u00f3n de pruebas existente<\/h3>\n<p>En ocasiones podr\u00eda resultarle muy \u00fatil efectuar una operaci\u00f3n de pruebas nueva que sea similar a una existente, pero que incluya algunos peque\u00f1os cambios (p. ej. un cambio en la versi\u00f3n asignada). El elemento de men\u00fa &#8220;Create Test Run copy&#8221; se ha creado espec\u00edficamente para esos casos. Este elemento del men\u00fa est\u00e1 disponible solo para incidencias de tipo &#8220;Test Run&#8221;, y desencadena las acciones siguientes:<\/p>\n<ul>\n<li>Crear una copia de la operaci\u00f3n de pruebas, as\u00ed como copias de las ejecuciones de casos de pruebas asignadas a ella.<\/li>\n<li>All newly created test case execution copies are assigned to the new test run using the \u2018parent-child\u2019 issue link type. Adem\u00e1s, estas copias se asignar\u00e1n a los casos de pruebas originales con el tipo de enlace de incidencia &#8220;Execution&#8221; (para mantener la trazabilidad).<\/li>\n<li>All statuses and statistics will be discarded for the newly created issue. <\/li>\n<\/ul>\n<p><img decoding=\"async\" src=\"https:\/\/resources.jetbrains.com\/storage\/products\/blog\/wp-content\/uploads\/done8s720.gif\" alt=\"\" \/><\/p>\n<p>La l\u00f3gica anterior se ha implementado utilizando la regla de acci\u00f3n &#8220;Create Test Run copy&#8221;.<\/p>\n<details>\n<summary>\n        Ampliar bloque de c\u00f3digo<br \/>\n    <\/summary>\n<p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-title=\"\">\nvar workflow = require(&#039;@jetbrains\/youtrack-scripting-api\/workflow&#039;);\nvar entities = require(&#039;@jetbrains\/youtrack-scripting-api\/entities&#039;);\nvar utils = require(&#039;..\/calculate-tms-stats\/utils&#039;);\n\nexports.rule = entities.Issue.action({\n  title: &#039;Create Test Run copy&#039;,\n  command: &#039;Test Run Creation&#039;,\n  guard: function(ctx) {\n    return ctx.issue.isReported &amp;&amp; (ctx.issue.Type.name == ctx.Type.TestRun.name);\n  },\n  action: function(ctx) {\n    var issue = ctx.issue;\n    var TestRunCopy = issue.copy(issue.project);\n    TestRunCopy.Status = ctx.Status.InProgress; \n    var oldTestList = issue.links[ctx.Subtask.outward];\n    oldTestList.forEach(function(v) {\n      var newTest = v.copy(v.project);\n      newTest.Status = ctx.Status.InProgress;\n      newTest.links[ctx.Subtask.inward].delete(issue);\n      newTest.links[ctx.Subtask.inward].add(TestRunCopy);\n    });\n    utils.resetStatuses(issue, TestRunCopy); \n    var newTestRunLink = &#039;&lt;a href=&quot;&#039; + TestRunCopy.url + &#039;&quot;&gt; &#039; + TestRunCopy.id + &#039;&lt;\/a&gt;&#039;;\n    var message = &#039;New Test Run has been created &#039; + newTestRunLink + &#039;.&#039;;\n    workflow.message(message);\n  },\n  requirements: {\n    Execution: {\n      type: entities.IssueLinkPrototype,\n      name: &#039;Execution&#039;,\n      inward: &#039;Execution&#039;,\n      outward: &#039;Assigned Test case or test suite&#039;\n    },\n    Subtask: {\n      type: entities.IssueLinkPrototype,\n      name: &#039;Subtask&#039;,\n      inward: &#039;subtask of&#039;,\n      outward: &#039;parent for&#039;\n    },\n    Type: {\n      type: entities.EnumField.fieldType,\n      TestRun: {\n        name: &quot;Test Run&quot;\n      },\n    },\n    Status: {\n      type: entities.EnumField.fieldType,\n      InProgress: {\n        name: &#039;No Run&#039;\n      },\n    }\n  }\n});\n <\/pre>\n<p>\n <\/details>\n<h2>Restringir las acciones de los usuarios<\/h2>\n<p>Para evitar potenciales equivocaciones al trabajar con escenarios de pruebas (p. ej. el uso de los tipos de incidencias err\u00f3neos), puede restringir posibles acciones de los usuarios. Por ejemplo, puede implementar acciones para asegurarse de que los usuarios vinculen casos de pruebas a operaciones de pruebas. When the user links a test run to an issue, this action will check whether the linked issue type is \u2018Test Case\u2019 or \u2018Test Suite.\u2019 Si no es ninguno de los dos, la acci\u00f3n enviar\u00e1 una advertencia que evita que el usuario continue.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/resources.jetbrains.com\/storage\/products\/blog\/wp-content\/uploads\/done9.720.gif\" alt=\"\" \/><\/p>\n<p>El siguiente bloque de c\u00f3digo puede a\u00f1adirse al flujo de trabajo &#8220;populate test run&#8221;.<\/p>\n<details>\n<summary>\n        Ampliar bloque de c\u00f3digo<br \/>\n    <\/summary>\n<p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-title=\"\">\n var message = &#039;&lt;a href=&quot;&#039; + TestCase.url + &#039;&quot;&gt; &#039; + TestCase.id + &#039;&lt;\/a&gt;&#039;;\n      workflow.check((TestCase.Type.name == ctx.Type.TestCase.name) || (TestCase.Type.name == ctx.Type.TestSuite.name), workflow.i18n(&#039;\\&#039;Test Run\\&#039; can be linked to \\&#039;Test Case\\&#039; and \\&#039;Test Suite\\&#039; only, but {0} has \\&#039;{1}\\&#039; type!&#039;, message, TestCase.Type.name));\n    <\/pre>\n<p>\n    <\/details>\n<p>Como se ha indicado <a href=\"#ref2\"> anteriormente<\/a>, el estado de la operaci\u00f3n de pruebas deber\u00eda ser de solo lectura, puesto que depende del progreso de las ejecuciones de las pruebas. Esto se puede lograr de un modo sencillo simplemente incluyendo un bloque de c\u00f3digo que restrinja los cambios de estado manuales para incidencias de tipo &#8220;Test Run&#8221;.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/09\/done10s720.gif\" alt=\"\" \/><br \/>\nPor ejemplo, la secci\u00f3n &#8216;transitions&#8217; del tipo de incidencia &#8216;Test Run&#8217; en el flujo de trabajo &#8220;status-management&#8221; se puede ajustar del modo siguiente:<\/p>\n<details>\n<summary>\n        Ampliar bloque de c\u00f3digo<br \/>\n    <\/summary>\n<p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-title=\"\">\n   &#039;Failing&#039;: {\n            targetState: &#039;Failing&#039;,\n            action: function(ctx) {\n              workflow.check(false, workflow.i18n(&#039;Test run has read-only status which is defined based on assigned tests statuses&#039;));\n            }\n          },\n          ...\n        }\n<\/pre>\n<p>\n<\/details>\n<h2>Informes y trazabilidad<\/h2>\n<h3>Creando informe<\/h3>\n<p>Una vez finalizado el ciclo de pruebas, probablemente desee analizar los resultados. Para ello puede utilizar la funcionalidad de informes de YouTrack. You can select the report type, which will display key information related to the test management procedure. Para esta demostraci\u00f3n incluiremos 2 informes en el panel:<\/p>\n<ul>\n<li>\n<em><b>Informe de flujo acumulativo con filtrado por campo &#8220;version&#8221;.<\/b><\/em> Este tipo de informe de estado hace referencia a una versi\u00f3n de producto espec\u00edfica. Le muestra m\u00e9tricas clave, como el n\u00famero de pruebas en estado &#8220;Passed&#8221;, &#8220;Failed&#8221; y &#8220;No Run&#8221;, distribu\u00eddas en una l\u00ednea temporal. Para mostrar datos de m\u00faltiples versiones, deber\u00e1 crear un informe para cada versi\u00f3n. Podr\u00eda resultar pr\u00e1ctico a\u00f1adir todos los informes al panel para que pueda trabajar con todos los datos en un solo lugar.<\/p>\n<h3>Configuraci\u00f3n del informe:<\/h3>\n<p><a href=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/09\/TMS11.png\" rel=\"attachment wp-att-6366\"><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/09\/TMS11.png\" alt=\"ApplyingCommandForSubsystem\" \/><\/a><\/p>\n<h3>Resultados del informe:<\/h3>\n<p><a href=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/09\/TMS12.png\" rel=\"attachment wp-att-6366\"><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/09\/TMS12.png\" alt=\"ApplyingCommandForSubsystem\" \/><\/a><\/p>\n<\/li>\n<li><em> <b>Informe de distribuci\u00f3n de incidencias.<\/b><\/em>  Este informe es una instant\u00e1nea que incluye m\u00e9tricas clave (como el n\u00famero de operaciones de pruebas con cada estado) para varias versiones del producto. Espec\u00edficamente para esta demo hemos incluido resultados de informes que pueden ayudarle a comprar versiones entre s\u00ed y analizar la estabilidad de las mismas.<br \/>\n<h3>Configuraci\u00f3n del informe:<\/h3>\n<p><a href=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/09\/TMS13.png\" rel=\"attachment wp-att-6366\"><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/09\/TMS13.png\" alt=\"ApplyingCommandForSubsystem\" \/><\/a><\/p>\n<h3>Resultados del informe:<\/h3>\n<p><a href=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/09\/re_res_cut.png\" rel=\"attachment wp-att-6366\"><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/09\/re_res_cut.png\" alt=\"ApplyingCommandForSubsystem\" \/><\/a>\n <\/li>\n<\/ul>\n<h2>Trazabilidad <\/h2>\n<p>Como YouTrack almacena informaci\u00f3n clave relacionada con los ciclos de pruebas para cada incidencia de tipo &#8220;Test Execution&#8221;, siempre podr\u00e1 acceder al historial de pruebas e identificar cualquier error relacionado con estas.<br \/>\nSi dispone de un caso de pruebas, puede consultar cualquier ejecuci\u00f3n de pruebas que lo incluyan.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/09\/TMS15.png\" alt=\"\" \/><\/p>\n<p>Con la ejecuci\u00f3n de casos de pruebas puede consultar una operaci\u00f3n de pruebas.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/09\/TMS16.png\" alt=\"\" \/><\/p>\n<p>Con una ejecuci\u00f3n de caso de pruebas fallida siempre puede consultar la lista de errores asociados.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/09\/TMS17.png\" alt=\"\" \/><\/p>\n<p>Para aprender m\u00e1s sobre los flujos de trabajo descritos en este art\u00edculo o para debatir otros temas relacionados con los flujos de trabajo, no dude en unirse a nuestra comunidad <a href=\"http:\/\/youtrack-community.herokuapp.com\/\" target=\"_blank\" rel=\"noopener\">YouTrack Workflow Community<\/a> en Slack.<\/p>\n","protected":false},"author":1086,"featured_media":83082,"comment_status":"closed","ping_status":"closed","template":"","categories":[1582,808],"tags":[1506,1510,1514,91,1545],"cross-post-tag":[],"acf":[],"_links":{"self":[{"href":"https:\/\/blog.jetbrains.com\/es\/wp-json\/wp\/v2\/youtrack\/98667"}],"collection":[{"href":"https:\/\/blog.jetbrains.com\/es\/wp-json\/wp\/v2\/youtrack"}],"about":[{"href":"https:\/\/blog.jetbrains.com\/es\/wp-json\/wp\/v2\/types\/youtrack"}],"author":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/es\/wp-json\/wp\/v2\/users\/1086"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/es\/wp-json\/wp\/v2\/comments?post=98667"}],"version-history":[{"count":1,"href":"https:\/\/blog.jetbrains.com\/es\/wp-json\/wp\/v2\/youtrack\/98667\/revisions"}],"predecessor-version":[{"id":98668,"href":"https:\/\/blog.jetbrains.com\/es\/wp-json\/wp\/v2\/youtrack\/98667\/revisions\/98668"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/es\/wp-json\/wp\/v2\/media\/83082"}],"wp:attachment":[{"href":"https:\/\/blog.jetbrains.com\/es\/wp-json\/wp\/v2\/media?parent=98667"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/es\/wp-json\/wp\/v2\/categories?post=98667"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/es\/wp-json\/wp\/v2\/tags?post=98667"},{"taxonomy":"cross-post-tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/es\/wp-json\/wp\/v2\/cross-post-tag?post=98667"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}