Я разбил это на два этапа: 1. сгенерировать древовидную структуру из входных данных, чтобы позволить подсчет числа строк; 2. Рисование строк и столбцов таблицы.
Весь код настолько универсален, насколько это возможно, чтобы избежать жестко закодированного количества столбцов.
Я также добавил динамический заголовок таблицы. Если вы добавите еще один столбец в структуру данных, заголовки таблицы HTML также будут обновлены. Например, можно использовать этот же код для добавления четвертого уровня, например, "LEVEL_4": "LEVEL 1.2.2.1"
в один или несколько рядов.
var data = [
{
"LEVEL_1": "LEVEL 1",
"LEVEL_2": "LEVEL 1.1",
"LEVEL_3": "LEVEL 1.1.1"
},
{
"LEVEL_1": "LEVEL 1",
"LEVEL_2": "LEVEL 1.2",
"LEVEL_3": "LEVEL 1.2.1"
},
{
"LEVEL_1": "LEVEL 1",
"LEVEL_2": "LEVEL 1.2",
"LEVEL_3": "LEVEL 1.2.2"
},
{
"LEVEL_1": "LEVEL 2",
"LEVEL_2": "LEVEL 2.1",
"LEVEL_3": "LEVEL 2.1.1"
},
{
"LEVEL_1": "LEVEL 2",
"LEVEL_2": "LEVEL 2.1",
"LEVEL_3": "LEVEL 2.1.2"
},
{
"LEVEL_1": "LEVEL 2",
"LEVEL_2": "LEVEL 2.2",
"LEVEL_3": "LEVEL 2.2.1"
},
{
"LEVEL_1": "LEVEL 2",
"LEVEL_2": "LEVEL 2.2",
"LEVEL_3": "LEVEL 2.2.2"
}
];
/**
* Generate rows and columns for the table using the JSON data.
*
* @param {Object} data
* @param {HTMLTableSectionElement} tableBody
* @param {HTMLTableSectionElement} tableHead
*/
function setTableContent(data, tableBody, tableHead) {
/* The rowspan is stored here depending on the label of each column */
var columnRowspans = {};
var columnHeadings = {};
/**
* Translate the data into a tree-like structure
*
* @param {JSON} data
* @return {Array}
*/
function translateData(data) {
var rows = [];
/* Iterate over each row in the dataset */
data.forEach(function (row) {
var columns = [],
label;
/* Find the individual columns */
for (var key in row) {
/* Store the columns header */
columnHeadings[key] = true;
label = row[key];
/* Skip those columns that were already added */
if (columnRowspans[label]) {
columnRowspans[label]++;
continue;
}
columns.push(label);
columnRowspans[label] = 1;
}
rows.push(columns);
});
return rows;
}
/* Template string used for a single field in the table */
var cellTemplate = '<td rowspan="{rowspan}">{content}</td>';
/* Output */
var html = '';
translateData(data).forEach(function (row, index) {
html += '<tr>';
row.forEach(function (columnLabel) {
/* Use the stored rowspans here to generate columns */
html += cellTemplate
.replace('{rowspan}', columnRowspans[columnLabel])
.replace('{content}', columnLabel);
});
html += '</tr>';
});
if (tableBody instanceof HTMLTableSectionElement) {
tableBody.innerHTML = html;
}
if (tableHead instanceof HTMLTableSectionElement) {
var thead = '<tr>';
Object.keys(columnHeadings).forEach(function (heading) {
thead += '<th>' + heading.replace('_', ' ') + '</th>';
});
thead += '</tr>';
tableHead.innerHTML = thead;
}
}
setTableContent(data, document.querySelector('#user tbody'), document.querySelector('#user thead'));
table {
border-collapse: collapse;
}
td {
padding: 20px;
border: 1px solid black;
text-align: center;
}
th {
padding: 20px;
border: 1px solid black;
text-align: center;
}
<table id="user">
<thead>
</thead>
<tbody>
</tbody>
</table>
Если Вы можете, самая лучшая вещь сделать, имеют систему, в которой каждый тест Селена получает свое собственное состояние данных (т.е.: Таблицы базы данных, отброшенные и воссозданные, загрузите данные, повторно вставленные, и очищенные кэши). Это легче сказать чем сделать и обычно только возможно если проект, запланированный его от запуска.
Следующая лучшая вещь состоит в том, чтобы иметь последовательное состояние БД для каждого набора тестов / выполненный. Это не столь хорошо, так как существует теперь сильный шанс, что некоторые тесты будут зависеть от успеха ранее запущенных тестов, делание его более трудный определяет истинные отказы по сравнению с ложными отрицательными сторонами.
Худший случай, IMO, должен использовать статический DB, в котором каждый тестовый прогон видоизменяет дату. Это почти всегда приводит к проблемам и обычно является "запахом проекта". Ключ к выполнению его "правильный путь" (снова, IMO) состоит в том, чтобы быть бдителен в отношении любого изменения состояния/схемы и получить его как часть автоматизированного теста/процесса сборки.
Направляющие уже делают хорошее задание с этим с Миграциями, поэтому используйте в своих интересах их! Не зная Вашей ситуации, я обычно подвергал бы сомнению потребность запустить тесты Селена против снимка полного DB. Большая часть DBS может (или если) быть дистиллированной вниз меньше чем к 1 МБ для автоматизированного тестирования, делая автоматизированные миграции схемы, и данные сбрасывают намного более эффективный.
Единственное время я видел "допустимые" основания крупного DBS для тестов Селена, - когда сам DB содержит большие блоки "логических данных", в которых данные влияют на поток приложения (думайте: управляемый данными UI).
Я думаю, что Вы задаете два вопроса здесь, которые переплетены поэтому, если я должен сломать его:
У Вас есть несколько альтернатив здесь, которые я долго обсудил ниже. Поскольку Вы упомянули Oracle, я использую технологии Oracle здесь, но то же самое верно для других платформ DB (например, Postgresql):
Надежда, которая помогает.
Я в настоящее время нахожусь на проекте с огромным набором тестов Селена - на самом деле, одна Сетка Селена была записана для - и наши тесты используют небольшой объем справочных данных (хотя мы не используем направляющие приспособления YAML), и возразите фабрикам для одноразовых данных, необходимых для конкретных тестов.
С другой стороны, на многих проектах направляющих ThoughtWorks я шел, мы записали сценарии регистрации, которые включают много рычагов перед фиксацией - например, запуская тесты прежде, чем позволить фиксацию. Одна вещь Вы могли бы рассмотреть попытку, пишет (или настраивает), подобный сценарий регистрации, который проверит на изменения схемы и перезагрузит справочные данные по мере необходимости.
Посмотрите, например, задачи фиксации граблей Paul Gross на GitHub.