Skip to content

Draft: Display code coverage % in jobs details page even if 0

What does this MR do and why?

This is a start at resolving #237931 by TDD. The jest output is currently:

 FAIL  spec/frontend/jobs/components/job_sidebar_details_container_spec.js
  Job Sidebar Details Container
    when no details are available
      ✓ should render an empty container (20 ms)
      ✓ should not render duration details when missing (4 ms)
      ✓ should not render erased_at details when missing (2 ms)
      ✓ should not render finished_at details when missing (3 ms)
      ✓ should not render queued details when missing (2 ms)
      ✓ should not render runner details when missing (2 ms)
      ✓ should not render coverage details when missing (3 ms)
    when some of the details are available
      ✓ uses duration to render job-Elapsed time: 6 seconds (10 ms)
      ✓ uses erased_at to render job-Erased: 3 weeks ago (4 ms)
      ✓ uses finished_at to render job-Finished: 3 weeks ago (4 ms)
      ✓ uses queued to render job-Queued: 9 seconds (3 ms)
      ✓ uses runner to render job-Runner: #1 (ABCDEFGH) local ci runner (4 ms)
# ==> ✕ uses coverage to render job-Coverage: 0% (5 ms)
      ✓ only renders tags (5 ms)
    when all the info are available
# ==> ✕ renders all the details components (10 ms)
      duration row
        ✓ renders all the details components (12 ms)
    timeout
      ✓ does not render if metadata is empty (3 ms)
      ✓ uses metadata to render timeout (5 ms)
      ✓ uses metadata to render timeout and the source (4 ms)
      ✓ should not render when no time is provided (5 ms)

  ● Job Sidebar Details Container › when some of the details are available › uses coverage to render job-Coverage: 0%

    expect(received).toHaveLength(expected)

    Expected length: 1
    Received length: 0
    Received object: {"selector": {"_Ctor": {}, "components": {"GlIcon": {"_compiled": true, "computed": {"iconSizeClass": [Function iconSizeClass], "spriteHref": [Function spriteHref]}, "props": {"ariaLabel": {"default": undefined, "required": false, "type": [Function String]}, "name": {"required": true, "type": [Function String], "validator": [Function iconValidator]}, "size": {"default": 16, "required": false, "type": [Function Number], "validator": [Function validator]}}, "render": [Function __vue_render__], "staticRenderFns": []}, "GlLink": {"_compiled": true, "components": {"BLink": [Function VueComponent]}, "mixins": [{"computed": {"safeLinkConfig": [Function safeLinkConfig]}, "directives": {"SafeLink": {"inserted": [Function transform], "update": [Function update]}}, "props": {"isUnsafeLink": {"default": false, "required": false, "type": [Function Boolean]}, "target": {"default": null, "required": false, "type": [Function String]}}}], "render": [Function __vue_render__], "staticRenderFns": []}}, "computed": {"hasHelpURL": [Function hasHelpURL], "hasTitle": [Function hasTitle]}, "name": "SidebarDetailRow", "props": {"helpUrl": {"default": "", "required": false, "type": [Function String]}, "title": {"default": "", "required": false, "type": [Function String]}, "value": {"required": true, "type": [Function String]}}, "render": [Function render], "staticRenderFns": []}}

      67 |       const detailsRow = findAllDetailsRow();
      68 |
    > 69 |       expect(detailsRow).toHaveLength(1);
         |                          ^
      70 |       expect(detailsRow.at(0).text()).toBe(value);
      71 |     });
      72 |

      at spec/frontend/jobs/components/job_sidebar_details_container_spec.js:69:26

  ● Job Sidebar Details Container › when all the info are available › renders all the details components

    expect(received).toHaveLength(expected)

    Expected length: 7
    Received length: 6
    Received object: {"selector": {"_Ctor": {"1375": [Function VueComponent]}, "components": {"GlIcon": {"_Ctor": {"1375": [Function VueComponent]}, "_compiled": true, "computed": {"iconSizeClass": [Function iconSizeClass], "spriteHref": [Function spriteHref]}, "props": {"ariaLabel": {"default": undefined, "required": false, "type": [Function String]}, "name": {"required": true, "type": [Function String], "validator": [Function iconValidator]}, "size": {"default": 16, "required": false, "type": [Function Number], "validator": [Function validator]}}, "render": [Function __vue_render__], "staticRenderFns": []}, "GlLink": {"_Ctor": {"1375": [Function VueComponent]}, "_compiled": true, "components": {"BLink": [Function VueComponent]}, "mixins": [{"computed": {"safeLinkConfig": [Function safeLinkConfig]}, "directives": {"SafeLink": {"inserted": [Function transform], "update": [Function update]}}, "props": {"isUnsafeLink": {"default": false, "required": false, "type": [Function Boolean]}, "target": {"default": null, "required": false, "type": [Function String]}}}], "render": [Function __vue_render__], "staticRenderFns": []}}, "computed": {"hasHelpURL": [Function hasHelpURL], "hasTitle": [Function hasTitle]}, "name": "SidebarDetailRow", "props": {"helpUrl": {"default": "", "required": false, "type": [Function String]}, "title": {"default": "", "required": false, "type": [Function String]}, "value": {"required": true, "type": [Function String]}}, "render": [Function render], "staticRenderFns": []}}

      85 |       await store.dispatch('receiveJobSuccess', job);
      86 |
    > 87 |       expect(findAllDetailsRow()).toHaveLength(7);
         |                                   ^
      88 |     });
      89 |
      90 |     describe('duration row', () => {

      at Object.<anonymous> (spec/frontend/jobs/components/job_sidebar_details_container_spec.js:87:35)

Test Suites: 1 failed, 1 total
Tests:       2 failed, 18 passed, 20 total
Snapshots:   0 total
Time:        3.076 s

Using RubyMine's debugger, I tried stepping through that test, but didn't find where exactly the 0 gets converted (to false or null as I hypothesised in #237931 (comment 804132733)).

I'm not sure how to proceed investigating this, but I wanted to record this first step somewhere.

Screenshots or screen recordings

These are strongly recommended to assist reviewers and reduce the time to merge your change.

How to set up and validate locally

yarn jest --watch job_sidebar_details_container_spec.js

MR acceptance checklist

This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.

Merge request reports