RequestRunner.ts 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. import { Observable } from "rxjs"
  2. import { filter } from "rxjs/operators"
  3. import getEnvironmentVariablesFromScript from "./preRequest"
  4. import { getEffectiveRESTRequest } from "./utils/EffectiveURL"
  5. import { HoppRESTResponse } from "./types/HoppRESTResponse"
  6. import { createRESTNetworkRequestStream } from "./network"
  7. import runTestScriptWithVariables, {
  8. transformResponseForTesting,
  9. } from "./postwomanTesting"
  10. import { HoppTestData, HoppTestResult } from "./types/HoppTestResult"
  11. import { getRESTRequest, setRESTTestResults } from "~/newstore/RESTSession"
  12. /**
  13. * Runs a REST network request along with all the
  14. * other side processes (like running test scripts)
  15. */
  16. export function runRESTRequest$(): Observable<HoppRESTResponse> {
  17. const envs = getEnvironmentVariablesFromScript(
  18. getRESTRequest().preRequestScript
  19. )
  20. const effectiveRequest = getEffectiveRESTRequest(getRESTRequest(), {
  21. name: "Env",
  22. variables: Object.keys(envs).map((key) => {
  23. return {
  24. key,
  25. value: envs[key],
  26. }
  27. }),
  28. })
  29. const stream = createRESTNetworkRequestStream(effectiveRequest)
  30. // Run Test Script when request ran successfully
  31. const subscription = stream
  32. .pipe(filter((res) => res.type === "success"))
  33. .subscribe((res) => {
  34. const testReport: {
  35. report: "" // ¯\_(ツ)_/¯
  36. testResults: Array<
  37. | {
  38. result: "FAIL"
  39. message: string
  40. styles: { icon: "close"; class: "cl-error-response" }
  41. }
  42. | {
  43. result: "PASS"
  44. message: string
  45. styles: { icon: "check"; class: "success-response" }
  46. }
  47. | { startBlock: string; styles: { icon: ""; class: "" } }
  48. | { endBlock: true; styles: { icon: ""; class: "" } }
  49. >
  50. errors: [] // ¯\_(ツ)_/¯
  51. } = runTestScriptWithVariables(effectiveRequest.testScript, {
  52. response: transformResponseForTesting(res),
  53. }) as any
  54. setRESTTestResults(translateToNewTestResults(testReport))
  55. subscription.unsubscribe()
  56. })
  57. return stream
  58. }
  59. function isTestPass(x: any): x is {
  60. result: "PASS"
  61. styles: { icon: "check"; class: "success-response" }
  62. } {
  63. return x.result !== undefined && x.result === "PASS"
  64. }
  65. function isTestFail(x: any): x is {
  66. result: "FAIL"
  67. message: string
  68. styles: { icon: "close"; class: "cl-error-response" }
  69. } {
  70. return x.result !== undefined && x.result === "FAIL"
  71. }
  72. function isStartBlock(
  73. x: any
  74. ): x is { startBlock: string; styles: { icon: ""; class: "" } } {
  75. return x.startBlock !== undefined
  76. }
  77. function isEndBlock(
  78. x: any
  79. ): x is { endBlock: true; styles: { icon: ""; class: "" } } {
  80. return x.endBlock !== undefined
  81. }
  82. function translateToNewTestResults(testReport: {
  83. report: "" // ¯\_(ツ)_/¯
  84. testResults: Array<
  85. | {
  86. result: "FAIL"
  87. message: string
  88. styles: { icon: "close"; class: "cl-error-response" }
  89. }
  90. | {
  91. result: "PASS"
  92. message: string
  93. styles: { icon: "check"; class: "success-response" }
  94. }
  95. | { startBlock: string; styles: { icon: ""; class: "" } }
  96. | { endBlock: true; styles: { icon: ""; class: "" } }
  97. >
  98. errors: [] // ¯\_(ツ)_/¯
  99. }): HoppTestResult {
  100. // Build a stack of test data which we eventually build up based on the results
  101. const testsStack: HoppTestData[] = [
  102. {
  103. description: "root",
  104. tests: [],
  105. expectResults: [],
  106. },
  107. ]
  108. testReport.testResults.forEach((result) => {
  109. // This is a test block start, push an empty test to the stack
  110. if (isStartBlock(result)) {
  111. testsStack.push({
  112. description: result.startBlock,
  113. tests: [],
  114. expectResults: [],
  115. })
  116. } else if (isEndBlock(result)) {
  117. // End of the block, pop the stack and add it as a child to the current stack top
  118. const testData = testsStack.pop()!
  119. testsStack[testsStack.length - 1].tests.push(testData)
  120. } else if (isTestPass(result)) {
  121. // A normal PASS expectation
  122. testsStack[testsStack.length - 1].expectResults.push({
  123. status: "pass",
  124. message: result.message,
  125. })
  126. } else if (isTestFail(result)) {
  127. // A normal FAIL expectation
  128. testsStack[testsStack.length - 1].expectResults.push({
  129. status: "fail",
  130. message: result.message,
  131. })
  132. }
  133. })
  134. // We should end up with only the root stack entry
  135. if (testsStack.length !== 1) throw new Error("Invalid test result structure")
  136. return {
  137. expectResults: testsStack[0].expectResults,
  138. tests: testsStack[0].tests,
  139. }
  140. }