Update ESLint configuration, package.json, and README; improve formatting and versioning consistency. Adjust TypeScript configuration to disallow importing extensions and refine test scripts for better readability.
This commit is contained in:
44
README.md
44
README.md
@@ -64,24 +64,24 @@ new HashMap<K, V>(
|
|||||||
|
|
||||||
### Methods
|
### Methods
|
||||||
|
|
||||||
| Method | Description | Time Complexity |
|
| Method | Description | Time Complexity |
|
||||||
|--------|-------------|-----------------|
|
| ------------------------------------- | --------------------------------- | --------------- |
|
||||||
| `set(key: K, value: V): void` | Insert or update a key-value pair | O(1) average |
|
| `set(key: K, value: V): void` | Insert or update a key-value pair | O(1) average |
|
||||||
| `get(key: K): V \| undefined` | Retrieve value by key | O(1) average |
|
| `get(key: K): V \| undefined` | Retrieve value by key | O(1) average |
|
||||||
| `has(key: K): boolean` | Check if key exists | O(1) average |
|
| `has(key: K): boolean` | Check if key exists | O(1) average |
|
||||||
| `delete(key: K): boolean` | Remove entry by key | O(1) average |
|
| `delete(key: K): boolean` | Remove entry by key | O(1) average |
|
||||||
| `clear(): void` | Remove all entries | O(n) |
|
| `clear(): void` | Remove all entries | O(n) |
|
||||||
| `keys(): IterableIterator<K>` | Iterator over keys | O(n) |
|
| `keys(): IterableIterator<K>` | Iterator over keys | O(n) |
|
||||||
| `values(): IterableIterator<V>` | Iterator over values | O(n) |
|
| `values(): IterableIterator<V>` | Iterator over values | O(n) |
|
||||||
| `entries(): IterableIterator<[K, V]>` | Iterator over entries | O(n) |
|
| `entries(): IterableIterator<[K, V]>` | Iterator over entries | O(n) |
|
||||||
| `forEach(callback): void` | Execute callback for each entry | O(n) |
|
| `forEach(callback): void` | Execute callback for each entry | O(n) |
|
||||||
|
|
||||||
### Properties
|
### Properties
|
||||||
|
|
||||||
| Property | Description |
|
| Property | Description |
|
||||||
|----------|-------------|
|
| ------------ | ------------------------------------- |
|
||||||
| `size` | Number of key-value pairs |
|
| `size` | Number of key-value pairs |
|
||||||
| `capacity` | Current number of buckets |
|
| `capacity` | Current number of buckets |
|
||||||
| `loadFactor` | Current load factor (size / capacity) |
|
| `loadFactor` | Current load factor (size / capacity) |
|
||||||
|
|
||||||
## Advanced Usage
|
## Advanced Usage
|
||||||
@@ -108,7 +108,7 @@ class CaseInsensitiveHashFunction implements IHashFunction<string> {
|
|||||||
const map = new HashMap<string, number>(
|
const map = new HashMap<string, number>(
|
||||||
16,
|
16,
|
||||||
0.75,
|
0.75,
|
||||||
new CaseInsensitiveHashFunction()
|
new CaseInsensitiveHashFunction(),
|
||||||
);
|
);
|
||||||
|
|
||||||
map.set("Hello", 1);
|
map.set("Hello", 1);
|
||||||
@@ -122,11 +122,7 @@ Use the built-in `NumericHashFunction` for better distribution with numeric keys
|
|||||||
```typescript
|
```typescript
|
||||||
import { HashMap, NumericHashFunction } from "@techniker-me/hash-map";
|
import { HashMap, NumericHashFunction } from "@techniker-me/hash-map";
|
||||||
|
|
||||||
const map = new HashMap<number, string>(
|
const map = new HashMap<number, string>(16, 0.75, new NumericHashFunction());
|
||||||
16,
|
|
||||||
0.75,
|
|
||||||
new NumericHashFunction()
|
|
||||||
);
|
|
||||||
|
|
||||||
map.set(12345, "value1");
|
map.set(12345, "value1");
|
||||||
map.set(67890, "value2");
|
map.set(67890, "value2");
|
||||||
@@ -155,25 +151,30 @@ console.log(user?.name); // "Alice"
|
|||||||
This implementation adheres to all five SOLID principles:
|
This implementation adheres to all five SOLID principles:
|
||||||
|
|
||||||
### 1. Single Responsibility Principle (SRP)
|
### 1. Single Responsibility Principle (SRP)
|
||||||
|
|
||||||
- `HashMap` - Manages hash map operations
|
- `HashMap` - Manages hash map operations
|
||||||
- `HashNode` - Stores key-value pairs
|
- `HashNode` - Stores key-value pairs
|
||||||
- `DefaultHashFunction` - Handles hashing logic
|
- `DefaultHashFunction` - Handles hashing logic
|
||||||
- Each class has one clear purpose
|
- Each class has one clear purpose
|
||||||
|
|
||||||
### 2. Open/Closed Principle (OCP)
|
### 2. Open/Closed Principle (OCP)
|
||||||
|
|
||||||
- Extensible through custom hash functions
|
- Extensible through custom hash functions
|
||||||
- Core implementation is closed for modification
|
- Core implementation is closed for modification
|
||||||
|
|
||||||
### 3. Liskov Substitution Principle (LSP)
|
### 3. Liskov Substitution Principle (LSP)
|
||||||
|
|
||||||
- All implementations correctly implement their interfaces
|
- All implementations correctly implement their interfaces
|
||||||
- Subtypes can replace their base types without breaking functionality
|
- Subtypes can replace their base types without breaking functionality
|
||||||
|
|
||||||
### 4. Interface Segregation Principle (ISP)
|
### 4. Interface Segregation Principle (ISP)
|
||||||
|
|
||||||
- `IHashMap` - Focused map operations
|
- `IHashMap` - Focused map operations
|
||||||
- `IHashFunction` - Minimal hashing interface
|
- `IHashFunction` - Minimal hashing interface
|
||||||
- Clients depend only on interfaces they use
|
- Clients depend only on interfaces they use
|
||||||
|
|
||||||
### 5. Dependency Inversion Principle (DIP)
|
### 5. Dependency Inversion Principle (DIP)
|
||||||
|
|
||||||
- Depends on `IHashFunction` abstraction, not concrete implementations
|
- Depends on `IHashFunction` abstraction, not concrete implementations
|
||||||
- High-level modules don't depend on low-level modules
|
- High-level modules don't depend on low-level modules
|
||||||
|
|
||||||
@@ -230,6 +231,7 @@ bun test --coverage
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Test Coverage: 100%** ✅
|
**Test Coverage: 100%** ✅
|
||||||
|
|
||||||
- 66 comprehensive tests
|
- 66 comprehensive tests
|
||||||
- 1,168 assertions
|
- 1,168 assertions
|
||||||
- All edge cases covered
|
- All edge cases covered
|
||||||
|
|||||||
@@ -8,11 +8,31 @@ import { defineConfig } from "eslint/config";
|
|||||||
|
|
||||||
export default defineConfig([
|
export default defineConfig([
|
||||||
{
|
{
|
||||||
ignores: ["dist/**", "node_modules/**", "*.min.js"]
|
ignores: ["dist/**", "node_modules/**", "*.min.js"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ["**/*.{js,mjs,cjs,ts,mts,cts}"],
|
||||||
|
plugins: { js },
|
||||||
|
extends: ["js/recommended"],
|
||||||
|
languageOptions: { globals: globals.node },
|
||||||
},
|
},
|
||||||
{ files: ["**/*.{js,mjs,cjs,ts,mts,cts}"], plugins: { js }, extends: ["js/recommended"], languageOptions: { globals: globals.node } },
|
|
||||||
tseslint.configs.recommended,
|
tseslint.configs.recommended,
|
||||||
{ files: ["**/*.json"], plugins: { json }, language: "json/json", extends: ["json/recommended"] },
|
{
|
||||||
{ files: ["**/*.md"], plugins: { markdown }, language: "markdown/commonmark", extends: ["markdown/recommended"] },
|
files: ["**/*.json"],
|
||||||
{ files: ["**/*.css"], plugins: { css }, language: "css/css", extends: ["css/recommended"] },
|
plugins: { json },
|
||||||
|
language: "json/json",
|
||||||
|
extends: ["json/recommended"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ["**/*.md"],
|
||||||
|
plugins: { markdown },
|
||||||
|
language: "markdown/commonmark",
|
||||||
|
extends: ["markdown/recommended"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ["**/*.css"],
|
||||||
|
plugins: { css },
|
||||||
|
language: "css/css",
|
||||||
|
extends: ["css/recommended"],
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
|
|||||||
22
package.json
22
package.json
@@ -1,17 +1,25 @@
|
|||||||
{
|
{
|
||||||
"name": "@techniker-me/hash-map",
|
"name": "@techniker-me/hash-map",
|
||||||
"version": "1.0.8",
|
"version": "1.0.9",
|
||||||
"description": "A robust HashMap implementation following OOP SOLID principles",
|
"description": "A robust HashMap implementation following OOP SOLID principles",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "browser/index.ts",
|
"main": "./node/index.js",
|
||||||
"module": "node/index.js",
|
"module": "./browser/index.js",
|
||||||
"types": "types/index.d.ts",
|
"types": "./types/index.d.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": {
|
".": {
|
||||||
"node": "node/index.js",
|
"types": "./types/index.d.ts",
|
||||||
"browser": "browser/index.js"
|
"node": {
|
||||||
|
"import": "./node/index.js",
|
||||||
|
"default": "./node/index.js"
|
||||||
|
},
|
||||||
|
"browser": {
|
||||||
|
"import": "./browser/index.js",
|
||||||
|
"default": "./browser/index.js"
|
||||||
|
},
|
||||||
|
"default": "./node/index.js"
|
||||||
},
|
},
|
||||||
"./types": "types/index.d.ts"
|
"./types": "./types/index.d.ts"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"node",
|
"node",
|
||||||
|
|||||||
@@ -13,4 +13,4 @@ if [ ! -d "${distDirectory}" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Preparing [package.json] to [${distDirectory}]"
|
echo "Preparing [package.json] to [${distDirectory}]"
|
||||||
jq '{name, version, author, type, types, exports, files, publishConfig}' "${packageJsonPath}" > "${distDirectory}/package.json"
|
jq '{name, version, author, type, main, module, types, exports, files, publishConfig}' "${packageJsonPath}" > "${distDirectory}/package.json"
|
||||||
|
|||||||
@@ -16,10 +16,13 @@ async function convertTAPToTeamCity() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const proc = Bun.spawn(["bun", "test", "--reporter=tap", ...process.argv.slice(2)], {
|
const proc = Bun.spawn(
|
||||||
stdout: "pipe",
|
["bun", "test", "--reporter=tap", ...process.argv.slice(2)],
|
||||||
stderr: "pipe",
|
{
|
||||||
});
|
stdout: "pipe",
|
||||||
|
stderr: "pipe",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const decoder = new TextDecoder();
|
const decoder = new TextDecoder();
|
||||||
let currentSuite = "Tests";
|
let currentSuite = "Tests";
|
||||||
@@ -46,7 +49,11 @@ async function convertTAPToTeamCity() {
|
|||||||
if (line.startsWith("# ")) {
|
if (line.startsWith("# ")) {
|
||||||
// Suite/describe block
|
// Suite/describe block
|
||||||
const suiteName = line.substring(2).trim();
|
const suiteName = line.substring(2).trim();
|
||||||
if (suiteName && !suiteName.startsWith("tests") && !suiteName.startsWith("pass")) {
|
if (
|
||||||
|
suiteName &&
|
||||||
|
!suiteName.startsWith("tests") &&
|
||||||
|
!suiteName.startsWith("pass")
|
||||||
|
) {
|
||||||
if (testCount > 0) {
|
if (testCount > 0) {
|
||||||
TeamcityReporter.reportSuiteEnd(currentSuite);
|
TeamcityReporter.reportSuiteEnd(currentSuite);
|
||||||
}
|
}
|
||||||
@@ -67,12 +74,11 @@ async function convertTAPToTeamCity() {
|
|||||||
|
|
||||||
if (not) {
|
if (not) {
|
||||||
// Test failed
|
// Test failed
|
||||||
TeamcityReporter.reportTestFailed(
|
TeamcityReporter.reportTestFailed(fullName, "Test failed", line, {
|
||||||
fullName,
|
comparisonFailure: false,
|
||||||
"Test failed",
|
expected: "",
|
||||||
line,
|
actual: "",
|
||||||
{ comparisonFailure: false, expected: "", actual: "" }
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TeamcityReporter.reportTestEnd(fullName);
|
TeamcityReporter.reportTestEnd(fullName);
|
||||||
@@ -96,4 +102,3 @@ convertTAPToTeamCity().catch((error) => {
|
|||||||
console.error("Error:", error);
|
console.error("Error:", error);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ const isTeamCity = process.env.TEAMCITY_VERSION !== undefined;
|
|||||||
|
|
||||||
// Strip ANSI color codes
|
// Strip ANSI color codes
|
||||||
function stripAnsi(str: string): string {
|
function stripAnsi(str: string): string {
|
||||||
|
// eslint-disable-next-line no-control-regex
|
||||||
return str.replace(/\x1b\[[0-9;]*m/g, "");
|
return str.replace(/\x1b\[[0-9;]*m/g, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,7 +47,10 @@ async function runTests() {
|
|||||||
|
|
||||||
// Check for test suite/describe blocks (files ending with .test.ts)
|
// Check for test suite/describe blocks (files ending with .test.ts)
|
||||||
if (line.match(/tests\/.+\.test\.ts:$/)) {
|
if (line.match(/tests\/.+\.test\.ts:$/)) {
|
||||||
const fileName = line.replace(":", "").replace("tests/", "").replace(".test.ts", "");
|
const fileName = line
|
||||||
|
.replace(":", "")
|
||||||
|
.replace("tests/", "")
|
||||||
|
.replace(".test.ts", "");
|
||||||
|
|
||||||
// End previous suite if exists
|
// End previous suite if exists
|
||||||
while (suiteStack.length > 0) {
|
while (suiteStack.length > 0) {
|
||||||
@@ -73,12 +77,11 @@ async function runTests() {
|
|||||||
if (failMatch) {
|
if (failMatch) {
|
||||||
const testName = failMatch[1].trim();
|
const testName = failMatch[1].trim();
|
||||||
TeamcityReporter.reportTestStart(testName);
|
TeamcityReporter.reportTestStart(testName);
|
||||||
TeamcityReporter.reportTestFailed(
|
TeamcityReporter.reportTestFailed(testName, "Test failed", line, {
|
||||||
testName,
|
comparisonFailure: false,
|
||||||
"Test failed",
|
expected: "",
|
||||||
line,
|
actual: "",
|
||||||
{ comparisonFailure: false, expected: "", actual: "" }
|
});
|
||||||
);
|
|
||||||
TeamcityReporter.reportTestEnd(testName);
|
TeamcityReporter.reportTestEnd(testName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { IHashMap } from "../interfaces/IHashMap.ts";
|
import type { IHashMap } from "../interfaces/IHashMap";
|
||||||
import type { IHashFunction } from "../interfaces/IHashFunction.ts";
|
import type { IHashFunction } from "../interfaces/IHashFunction";
|
||||||
import { HashNode } from "../models/HashNode.ts";
|
import { HashNode } from "../models/HashNode";
|
||||||
import { DefaultHashFunction } from "../hash-functions/DefaultHashFunction.ts";
|
import { DefaultHashFunction } from "../hash-functions/DefaultHashFunction";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HashMap implementation using separate chaining for collision resolution.
|
* HashMap implementation using separate chaining for collision resolution.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { IHashFunction } from "../interfaces/IHashFunction.ts";
|
import type { IHashFunction } from "../interfaces/IHashFunction";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default hash function implementation.
|
* Default hash function implementation.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { IHashFunction } from "../interfaces/IHashFunction.ts";
|
import type { IHashFunction } from "../interfaces/IHashFunction";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specialized hash function for numeric keys.
|
* Specialized hash function for numeric keys.
|
||||||
|
|||||||
12
src/index.ts
12
src/index.ts
@@ -19,15 +19,15 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Core implementation
|
// Core implementation
|
||||||
export { HashMap } from "./core/HashMap.ts";
|
export { HashMap } from "./core/HashMap";
|
||||||
|
|
||||||
// Interfaces
|
// Interfaces
|
||||||
export type { IHashMap } from "./interfaces/IHashMap.ts";
|
export type { IHashMap } from "./interfaces/IHashMap";
|
||||||
export type { IHashFunction } from "./interfaces/IHashFunction.ts";
|
export type { IHashFunction } from "./interfaces/IHashFunction";
|
||||||
|
|
||||||
// Models
|
// Models
|
||||||
export { HashNode } from "./models/HashNode.ts";
|
export { HashNode } from "./models/HashNode";
|
||||||
|
|
||||||
// Hash functions
|
// Hash functions
|
||||||
export { DefaultHashFunction } from "./hash-functions/DefaultHashFunction.ts";
|
export { DefaultHashFunction } from "./hash-functions/DefaultHashFunction";
|
||||||
export { NumericHashFunction } from "./hash-functions/NumericHashFunction.ts";
|
export { NumericHashFunction } from "./hash-functions/NumericHashFunction";
|
||||||
|
|||||||
@@ -76,9 +76,9 @@ describe("DefaultHashFunction", () => {
|
|||||||
name: "Bob",
|
name: "Bob",
|
||||||
address: {
|
address: {
|
||||||
city: "NYC",
|
city: "NYC",
|
||||||
zip: "10001"
|
zip: "10001",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
const hash = hashFn.hash(nested, capacity);
|
const hash = hashFn.hash(nested, capacity);
|
||||||
|
|
||||||
@@ -321,4 +321,3 @@ describe("NumericHashFunction", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -253,7 +253,7 @@ describe("HashMap", () => {
|
|||||||
const numMap = new HashMap<number, string>(
|
const numMap = new HashMap<number, string>(
|
||||||
16,
|
16,
|
||||||
0.75,
|
0.75,
|
||||||
new NumericHashFunction()
|
new NumericHashFunction(),
|
||||||
);
|
);
|
||||||
|
|
||||||
numMap.set(123, "value1");
|
numMap.set(123, "value1");
|
||||||
@@ -273,7 +273,7 @@ describe("HashMap", () => {
|
|||||||
const customMap = new HashMap<string, string>(
|
const customMap = new HashMap<string, string>(
|
||||||
8,
|
8,
|
||||||
0.75,
|
0.75,
|
||||||
new SimpleHashFunction()
|
new SimpleHashFunction(),
|
||||||
);
|
);
|
||||||
|
|
||||||
customMap.set("hi", "short");
|
customMap.set("hi", "short");
|
||||||
@@ -359,4 +359,3 @@ describe("HashMap", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,60 +1,79 @@
|
|||||||
|
|
||||||
export class TeamcityReporter {
|
export class TeamcityReporter {
|
||||||
/**
|
/**
|
||||||
* Escape special characters for TeamCity service messages
|
* Escape special characters for TeamCity service messages
|
||||||
* https://www.jetbrains.com/help/teamcity/service-messages.html#Escaped+values
|
* https://www.jetbrains.com/help/teamcity/service-messages.html#Escaped+values
|
||||||
*/
|
*/
|
||||||
private static escape(str: string): string {
|
private static escape(str: string): string {
|
||||||
return str
|
return str
|
||||||
.replace(/\|/g, "||")
|
.replace(/\|/g, "||")
|
||||||
.replace(/'/g, "|'")
|
.replace(/'/g, "|'")
|
||||||
.replace(/\n/g, "|n")
|
.replace(/\n/g, "|n")
|
||||||
.replace(/\r/g, "|r")
|
.replace(/\r/g, "|r")
|
||||||
.replace(/\[/g, "|[")
|
.replace(/\[/g, "|[")
|
||||||
.replace(/]/g, "|]");
|
.replace(/]/g, "|]");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static reportSuiteStart(suiteName: string): void {
|
||||||
|
console.log(
|
||||||
|
`##teamcity[testSuiteStarted name='${this.escape(suiteName)}']`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static reportSuiteEnd(suiteName: string): void {
|
||||||
|
console.log(
|
||||||
|
`##teamcity[testSuiteFinished name='${this.escape(suiteName)}']`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static reportTestStart(testName: string): void {
|
||||||
|
console.log(`##teamcity[testStarted name='${this.escape(testName)}']`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static reportTestFailed(
|
||||||
|
testName: string,
|
||||||
|
failureMessage: string,
|
||||||
|
details: string,
|
||||||
|
{
|
||||||
|
comparisonFailure,
|
||||||
|
expected,
|
||||||
|
actual,
|
||||||
|
}: { comparisonFailure: boolean; expected: string; actual: string },
|
||||||
|
): void {
|
||||||
|
const attrs = [
|
||||||
|
`name='${this.escape(testName)}'`,
|
||||||
|
`message='${this.escape(failureMessage)}'`,
|
||||||
|
`details='${this.escape(details)}'`,
|
||||||
|
];
|
||||||
|
|
||||||
|
if (comparisonFailure) {
|
||||||
|
attrs.push(`type='comparisonFailure'`);
|
||||||
|
attrs.push(`expected='${this.escape(expected)}'`);
|
||||||
|
attrs.push(`actual='${this.escape(actual)}'`);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static reportSuiteStart(suiteName: string): void {
|
console.log(`##teamcity[testFailed ${attrs.join(" ")}]`);
|
||||||
console.log(`##teamcity[testSuiteStarted name='${this.escape(suiteName)}']`);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public static reportSuiteEnd(suiteName: string): void {
|
public static reportTestEnd(testName: string, duration?: number): void {
|
||||||
console.log(`##teamcity[testSuiteFinished name='${this.escape(suiteName)}']`);
|
const durationAttr =
|
||||||
}
|
duration !== undefined ? ` duration='${duration}'` : "";
|
||||||
|
console.log(
|
||||||
|
`##teamcity[testFinished name='${this.escape(testName)}'${durationAttr}]`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public static reportTestStart(testName: string): void {
|
public static reportTestError(testName: string, error: Error): void {
|
||||||
console.log(`##teamcity[testStarted name='${this.escape(testName)}']`);
|
const message = this.escape(error.message || "Unknown error");
|
||||||
}
|
const details = this.escape(error.stack || "");
|
||||||
|
console.log(
|
||||||
|
`##teamcity[testFailed name='${this.escape(testName)}' message='${message}' details='${details}']`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public static reportTestFailed(testName: string, failureMessage: string, details: string, {comparisonFailure, expected, actual}: {comparisonFailure: boolean, expected: string, actual: string}): void {
|
public static reportTestIgnored(testName: string, message?: string): void {
|
||||||
const attrs = [
|
const msgAttr = message ? ` message='${this.escape(message)}'` : "";
|
||||||
`name='${this.escape(testName)}'`,
|
console.log(
|
||||||
`message='${this.escape(failureMessage)}'`,
|
`##teamcity[testIgnored name='${this.escape(testName)}'${msgAttr}]`,
|
||||||
`details='${this.escape(details)}'`
|
);
|
||||||
];
|
}
|
||||||
|
|
||||||
if (comparisonFailure) {
|
|
||||||
attrs.push(`type='comparisonFailure'`);
|
|
||||||
attrs.push(`expected='${this.escape(expected)}'`);
|
|
||||||
attrs.push(`actual='${this.escape(actual)}'`);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`##teamcity[testFailed ${attrs.join(' ')}]`);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static reportTestEnd(testName: string, duration?: number): void {
|
|
||||||
const durationAttr = duration !== undefined ? ` duration='${duration}'` : '';
|
|
||||||
console.log(`##teamcity[testFinished name='${this.escape(testName)}'${durationAttr}]`);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static reportTestError(testName: string, error: Error): void {
|
|
||||||
const message = this.escape(error.message || 'Unknown error');
|
|
||||||
const details = this.escape(error.stack || '');
|
|
||||||
console.log(`##teamcity[testFailed name='${this.escape(testName)}' message='${message}' details='${details}']`);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static reportTestIgnored(testName: string, message?: string): void {
|
|
||||||
const msgAttr = message ? ` message='${this.escape(message)}'` : '';
|
|
||||||
console.log(`##teamcity[testIgnored name='${this.escape(testName)}'${msgAttr}]`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -39,7 +39,7 @@ export function setupTeamCityReporting() {
|
|||||||
name: string,
|
name: string,
|
||||||
message: string,
|
message: string,
|
||||||
details: string,
|
details: string,
|
||||||
comparison?: { expected: string; actual: string }
|
comparison?: { expected: string; actual: string },
|
||||||
) => {
|
) => {
|
||||||
TeamcityReporter.reportTestFailed(name, message, details, {
|
TeamcityReporter.reportTestFailed(name, message, details, {
|
||||||
comparisonFailure: !!comparison,
|
comparisonFailure: !!comparison,
|
||||||
@@ -52,4 +52,3 @@ export function setupTeamCityReporting() {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
|
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"allowImportingTsExtensions": true,
|
"allowImportingTsExtensions": false,
|
||||||
"allowArbitraryExtensions": true,
|
"allowArbitraryExtensions": false,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"emitDeclarationOnly": true,
|
"emitDeclarationOnly": true,
|
||||||
@@ -22,5 +22,5 @@
|
|||||||
"noPropertyAccessFromIndexSignature": false
|
"noPropertyAccessFromIndexSignature": false
|
||||||
},
|
},
|
||||||
"include": ["src"],
|
"include": ["src"],
|
||||||
"exclude": ["dist", "test"]
|
"exclude": ["dist", "test", "src/examples"]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user