(options =>
+ options.UseSqlite("Data Source=mynunit.db"));
+
+builder.Services.AddControllers();
+
+var app = builder.Build();
+
+app.UseCors("AllowAll");
+app.UseHttpsRedirection();
+app.MapControllers();
+
+app.Run();
\ No newline at end of file
diff --git a/HW6/MyNunitWeb.Api/Properties/launchSettings.json b/HW6/MyNunitWeb.Api/Properties/launchSettings.json
new file mode 100644
index 0000000..f91a8f9
--- /dev/null
+++ b/HW6/MyNunitWeb.Api/Properties/launchSettings.json
@@ -0,0 +1,23 @@
+{
+ "$schema": "https://json.schemastore.org/launchsettings.json",
+ "profiles": {
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": false,
+ "applicationUrl": "http://localhost:5091",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "https": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": false,
+ "applicationUrl": "https://localhost:7298;http://localhost:5091",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/HW6/MyNunitWeb.Api/appsettings.Development.json b/HW6/MyNunitWeb.Api/appsettings.Development.json
new file mode 100644
index 0000000..0c208ae
--- /dev/null
+++ b/HW6/MyNunitWeb.Api/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/HW6/MyNunitWeb.Api/appsettings.json b/HW6/MyNunitWeb.Api/appsettings.json
new file mode 100644
index 0000000..10f68b8
--- /dev/null
+++ b/HW6/MyNunitWeb.Api/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/HW6/MyNunitWeb.Api/stylecop.json b/HW6/MyNunitWeb.Api/stylecop.json
new file mode 100644
index 0000000..9bb4698
--- /dev/null
+++ b/HW6/MyNunitWeb.Api/stylecop.json
@@ -0,0 +1,9 @@
+{
+ "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
+ "settings": {
+ "documentationRules": {
+ "companyName": "khusainovilas",
+ "copyrightText": "Copyright (c) {companyName}. All rights reserved."
+ }
+ }
+}
\ No newline at end of file
diff --git a/HW6/MyNunitWeb.Client/index.html b/HW6/MyNunitWeb.Client/index.html
new file mode 100644
index 0000000..e202e29
--- /dev/null
+++ b/HW6/MyNunitWeb.Client/index.html
@@ -0,0 +1,61 @@
+
+
+
+
+
+ MyNUnit Web
+
+
+
+
+
MyNUnitWeb
+
+
+ Upload Assemblies and Run Tests
+
+
+
+
+ Current Run Results
+
+
+
+
+ | Test |
+ Status |
+ Time (ms) |
+ Message |
+
+
+
+
+
+
+
+
+
+ Run Details
+
+
+
+
+ | Test |
+ Status |
+ Time (ms) |
+ Message |
+
+
+
+
+
+
+
+
+
+
diff --git a/HW6/MyNunitWeb.Client/script.js b/HW6/MyNunitWeb.Client/script.js
new file mode 100644
index 0000000..659fb4e
--- /dev/null
+++ b/HW6/MyNunitWeb.Client/script.js
@@ -0,0 +1,106 @@
+const API_BASE = 'http://localhost:5091/api/test';
+
+document.addEventListener('DOMContentLoaded', () => {
+
+ const form = document.getElementById('uploadForm');
+ if (!form) {
+ return;
+ }
+
+ form.addEventListener('submit', async (e) => {
+ e.preventDefault();
+
+ const formData = new FormData(form);
+
+ try {
+ const res = await fetch(`${API_BASE}/run`, {
+ method: 'POST',
+ body: formData
+ });
+
+ if (res.ok) {
+ const run = await res.json();
+
+ document.getElementById('currentSummary').textContent =
+ `${new Date(run.timestamp).toLocaleString()} — ${run.assemblyNames}`;
+
+ fillTable('currentTable', run.results);
+ document.getElementById('currentResults').style.display = 'block';
+
+ loadHistory();
+ form.reset();
+ } else {
+ const errorText = await res.text();
+ console.error('Server error:', res.status, errorText);
+ alert('Failed to run tests: ' + res.status);
+ }
+ } catch (err) {
+ console.error('Network error:', err);
+ }
+ });
+
+ loadHistory();
+});
+
+async function loadHistory() {
+ try {
+ const res = await fetch(`${API_BASE}/history`);
+ if (!res.ok) throw new Error('Failed to load history');
+ const runs = await res.json();
+
+ const list = document.getElementById('historyList');
+ list.innerHTML = '';
+
+ runs.forEach(run => {
+ const li = document.createElement('li');
+ li.innerHTML = `
+ ${new Date(run.timestamp).toLocaleString()}
+ Assemblies: ${run.assemblyNames}
+ Passed: ${run.passed} | Failed: ${run.failed} | Ignored: ${run.ignored}
+ `;
+ li.onclick = () => showDetails(run.id);
+ list.appendChild(li);
+ });
+ } catch (err) {
+ console.error('Error loading history:', err);
+ }
+}
+
+async function showDetails(id) {
+ try {
+ const res = await fetch(`${API_BASE}/run/${id}`);
+ const run = await res.json();
+
+ document.getElementById('detailsSummary').textContent =
+ `${new Date(run.timestamp).toLocaleString()} — ${run.assemblyNames}`;
+
+ fillTable('detailsTable', run.results);
+ document.getElementById('details').style.display = 'block';
+ } catch (err) {
+ console.error('Error:', err);
+ }
+}
+
+function fillTable(tableId, results) {
+ const tbody = document.querySelector(`#${tableId} tbody`);
+ tbody.innerHTML = '';
+
+ results.forEach(r => {
+ const tr = document.createElement('tr');
+
+ let time;
+ if (r.status === 'Ignored') {
+ time = 0;
+ } else {
+ time = r.duration < 0.01 ? '<0.01' : r.duration.toFixed(2);
+ }
+
+ tr.innerHTML = `
+ ${r.testName.split(".").pop()} |
+ ${r.status} |
+ ${time} |
+ ${r.message || ''} |
+ `;
+ tbody.appendChild(tr);
+ });
+}
\ No newline at end of file
diff --git a/HW6/MyNunitWeb.Client/style.css b/HW6/MyNunitWeb.Client/style.css
new file mode 100644
index 0000000..a8359c3
--- /dev/null
+++ b/HW6/MyNunitWeb.Client/style.css
@@ -0,0 +1,109 @@
+body {
+ font-family: 'Segoe UI', Arial, sans-serif;
+ background: #f0f2f5;
+ color: #333;
+ margin: 0;
+ padding: 20px;
+}
+
+.app {
+ max-width: 1200px;
+ margin: 0 auto;
+}
+
+.app__title {
+ text-align: center;
+ color: #1a1a1a;
+ margin-bottom: 40px;
+}
+
+.card {
+ padding: 30px;
+ margin-bottom: 40px;
+ border-radius: 12px;
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.08);
+}
+
+.card_history .history_list {
+ max-height: 400px;
+ overflow-y: auto;
+ padding-right: 10px;
+}
+
+.card__title {
+ margin-top: 0;
+ color: #2c3e50;
+ border-bottom: 2px solid #0066cc;
+ padding-bottom: 10px;
+}
+
+.upload_form__input {
+ display: block;
+ margin: 20px 0;
+ padding: 10px;
+ font-size: 16px;
+}
+
+.upload_form__button {
+ padding: 14px 32px;
+ background: #0066cc;
+ color: white;
+ border: none;
+ border-radius: 8px;
+ font-size: 16px;
+ cursor: pointer;
+ transition: background 0.3s;
+}
+
+.upload_form__button:hover {
+ background: #0050a0;
+}
+
+.results_table {
+ width: 100%;
+ border-collapse: collapse;
+ margin-top: 20px;
+}
+
+.results_table th,
+.results_table td {
+ padding: 14px;
+ text-align: left;
+ border-bottom: 1px solid #eee;
+}
+
+.results_table th {
+ background: #f8f9fa;
+ font-weight: 600;
+}
+
+.summary {
+ font-size: 18px;
+ font-weight: bold;
+ color: #2c3e50;
+ margin-bottom: 20px;
+}
+
+.history_list {
+ list-style: none;
+ padding: 0;
+}
+
+.history_list li {
+ padding: 20px;
+ background: #f8f9fa;
+ margin: 12px 0;
+ border-radius: 10px;
+ border-left: 6px solid #0066cc;
+ cursor: pointer;
+ transition: all 0.2s;
+}
+
+.history_list li:hover {
+ background: #e8f4ff;
+ transform: translateX(5px);
+}
+
+.passed { color: green; font-weight: bold; }
+.failed { color: red; font-weight: bold; }
+.ignored { color: orange; font-weight: bold; }
\ No newline at end of file