• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MarkUsProject / Markus / 20243384039

11 Dec 2025 06:21PM UTC coverage: 91.513%. First build
20243384039

Pull #7763

github

web-flow
Merge 9f55e660a into 0d3f24005
Pull Request #7763: Release 2.9.0

914 of 1805 branches covered (50.64%)

Branch coverage included in aggregate %.

1584 of 1666 new or added lines in 108 files covered. (95.08%)

43650 of 46892 relevant lines covered (93.09%)

121.64 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

69.4
/app/javascript/Components/test_group_result_table.jsx
1
import React from "react";
2
import ReactTable from "react-table";
3
import {selectFilter} from "./Helpers/table_helpers";
4
import {TestGroupFeedbackFileTable} from "./test_group_feedback_file_table";
5

6
export class TestGroupResultTable extends React.Component {
7
  constructor(props) {
8
    super(props);
9✔
9
    this.state = {
9✔
10
      show_output: this.showOutput(props.data),
11
      expanded: this.computeExpanded(props.data),
12
      filtered: [],
13
      filteredData: props.data,
14
    };
15
  }
16

17
  componentDidUpdate(prevProps) {
NEW
18
    if (prevProps.data !== this.props.data) {
×
NEW
19
      this.setState({filteredData: this.props.data, filtered: []});
×
20
    }
21
  }
22

23
  computeExpanded = data => {
9✔
24
    let expanded = {};
9✔
25
    let i = 0;
9✔
26
    let groups = new Set();
9✔
27
    data.forEach(row => {
9✔
28
      if (!groups.has(row["test_groups.id"])) {
9!
29
        expanded[i] = {};
9✔
30
        i++;
9✔
31
        groups.add(row["test_groups.id"]);
9✔
32
      }
33
    });
34
    return expanded;
9✔
35
  };
36

37
  onExpandedChange = newExpanded => {
9✔
NEW
38
    this.setState({expanded: newExpanded});
×
39
  };
40

41
  showOutput = data => {
9✔
42
    if (data) {
9!
43
      return data.some(row => "test_results.output" in row);
9✔
44
    } else {
NEW
45
      return false;
×
46
    }
47
  };
48

49
  columns = () => [
9✔
50
    {
51
      id: "test_group_id",
52
      Header: "",
53
      accessor: row => row["test_groups.id"],
9✔
54
      maxWidth: 30,
55
    },
56
    {
57
      id: "test_group_name",
58
      Header: "",
59
      accessor: row => row["test_groups.name"],
9✔
60
      maxWidth: 30,
61
      show: false,
62
    },
63
    {
64
      id: "name",
65
      Header: I18n.t("activerecord.attributes.test_result.name"),
66
      accessor: row => row["test_results.name"],
9✔
67
      aggregate: (values, rows) => {
68
        if (rows.length === 0) {
9!
NEW
69
          return "";
×
70
        } else {
71
          return rows[0]["test_group_name"];
9✔
72
        }
73
      },
74
      minWidth: 200,
75
    },
76
    {
77
      id: "test_status",
78
      Header: I18n.t("activerecord.attributes.test_result.status"),
79
      accessor: "test_results_status",
80
      width: 80,
81
      aggregate: (vals, rows) => {
82
        const hasTimeout = rows.some(
9✔
83
          row => row._original["test_group_results.error_type"] === "timeout"
9✔
84
        );
85
        return hasTimeout ? "timeout" : "";
9✔
86
      },
87
      Aggregated: row => {
88
        return row.value === "timeout"
9✔
89
          ? I18n.t("activerecord.attributes.test_group_result.timeout")
90
          : "";
91
      },
92
      filterable: true,
93
      Filter: selectFilter,
94
      filterOptions: ["pass", "partial", "fail", "error", "error_all"].map(status => ({
45✔
95
        value: status,
96
        text: status,
97
      })),
98
      // Disable the default filter method because this is a controlled component
NEW
99
      filterMethod: () => true,
×
100
    },
101
    {
102
      id: "marks_earned",
103
      Header: I18n.t("activerecord.attributes.test_result.marks_earned"),
104
      accessor: row => row["test_results.marks_earned"],
9✔
105
      Cell: row => {
106
        const marksEarned = row.original["test_results.marks_earned"];
9✔
107
        const marksTotal = row.original["test_results.marks_total"];
9✔
108
        if (marksEarned !== null && marksTotal !== null) {
9!
109
          return `${marksEarned} / ${marksTotal}`;
9✔
110
        } else {
NEW
111
          return "";
×
112
        }
113
      },
114
      width: 80,
115
      className: "number",
116
      aggregate: (vals, rows) =>
117
        rows.reduce(
9✔
118
          (acc, row) => [
9✔
119
            acc[0] + (row._original["test_results.marks_earned"] || 0),
12✔
120
            acc[1] + (row._original["test_results.marks_total"] || 0),
11✔
121
          ],
122
          [0, 0]
123
        ),
124
      Aggregated: row => {
125
        const timeout_reached = row.value[0] === 0 && row.value[1] === 0;
9✔
126
        const ret_val = timeout_reached
9✔
127
          ? I18n.t("activerecord.attributes.test_group_result.no_test_results")
128
          : `${row.value[0]} / ${row.value[1]}`;
129
        return ret_val;
9✔
130
      },
131
    },
132
  ];
133

134
  filterByStatus = filtered => {
9✔
135
    let status;
NEW
136
    for (const filter of filtered) {
×
NEW
137
      if (filter.id === "test_status") {
×
NEW
138
        status = filter.value;
×
139
      }
140
    }
141

142
    let filteredData;
NEW
143
    if (!!status && status !== "all") {
×
NEW
144
      filteredData = this.props.data.filter(row => row.test_results_status === status);
×
145
    } else {
NEW
146
      filteredData = this.props.data;
×
147
    }
148

NEW
149
    this.setState({
×
150
      filtered,
151
      filteredData,
152
      expanded: this.computeExpanded(filteredData),
153
    });
154
  };
155

156
  render() {
157
    const seen = new Set();
9✔
158
    const extraInfo = this.props.data
9✔
159
      .reduce((acc, test_data) => {
160
        const id = test_data["test_groups.id"];
9✔
161
        const name = (test_data["test_groups.name"] || "").trim();
9!
162
        const info = (test_data["test_group_results.extra_info"] || "").trim();
9✔
163

164
        if (!seen.has(id) && info) {
9!
NEW
165
          seen.add(id);
×
NEW
166
          acc.push(`[${name}]`, info);
×
167
        }
168

169
        return acc;
9✔
170
      }, [])
171
      .join("\n");
172
    let extraInfoDisplay;
173
    if (extraInfo) {
9!
NEW
174
      extraInfoDisplay = (
×
175
        <div>
176
          <h4>{I18n.t("activerecord.attributes.test_group_result.extra_info")}</h4>
177
          <pre>{extraInfo}</pre>
178
        </div>
179
      );
180
    } else {
181
      extraInfoDisplay = "";
9✔
182
    }
183
    const feedbackFiles = [];
9✔
184
    this.props.data.forEach(data => {
9✔
185
      data.feedback_files.forEach(feedbackFile => {
9✔
NEW
186
        if (!feedbackFiles.some(f => f.id === feedbackFile.id)) {
×
NEW
187
          feedbackFiles.push(feedbackFile);
×
188
        }
189
      });
190
    });
191
    let feedbackFileDisplay;
192
    if (feedbackFiles.length) {
9!
NEW
193
      feedbackFileDisplay = (
×
194
        <TestGroupFeedbackFileTable data={feedbackFiles} course_id={this.props.course_id} />
195
      );
196
    } else {
197
      feedbackFileDisplay = "";
9✔
198
    }
199

200
    return (
9✔
201
      <div>
202
        <ReactTable
203
          className={this.state.loading ? "auto-overflow" : "auto-overflow display-block"}
9!
204
          data={this.state.filteredData}
205
          columns={this.columns()}
206
          pivotBy={["test_group_id"]}
207
          getTdProps={(state, rowInfo) => {
208
            if (rowInfo) {
945✔
209
              let className = `-wrap test-result-${rowInfo.row["test_status"]}`;
90✔
210
              if (
90✔
211
                !rowInfo.aggregated &&
135!
212
                (!this.state.show_output || !rowInfo.original["test_results.output"])
213
              ) {
214
                className += " hide-rt-expander";
45✔
215
              }
216
              return {className: className};
90✔
217
            } else {
218
              return {};
855✔
219
            }
220
          }}
221
          PivotValueComponent={() => ""}
9✔
222
          expanded={this.state.expanded}
223
          filtered={this.state.filtered}
224
          onFilteredChange={this.filterByStatus}
225
          onExpandedChange={this.onExpandedChange}
226
          collapseOnDataChange={false}
227
          collapseOnSortingChange={false}
228
          SubComponent={row => (
NEW
229
            <pre className={`test-results-output test-result-${row.row["test_status"]}`}>
×
230
              {row.original["test_results.output"]}
231
            </pre>
232
          )}
233
          style={{maxHeight: "initial"}}
234
        />
235
        {extraInfoDisplay}
236
        {feedbackFileDisplay}
237
      </div>
238
    );
239
  }
240
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc