Skip to content

Commit

Permalink
Merge pull request #1 from nissy-dev/implemente-tenbin-jest
Browse files Browse the repository at this point in the history
Implemente @tenbin/jest
  • Loading branch information
nissy-dev authored Oct 5, 2024
2 parents e235d76 + 8023edc commit 7e3959d
Show file tree
Hide file tree
Showing 16 changed files with 378 additions and 21 deletions.
1 change: 1 addition & 0 deletions .github/actions/setup/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ runs:
- name: Use Node.js
uses: actions/setup-node@v4
with:
registry-url: "https://registry.npmjs.org"
node-version-file: ".tool-versions"
cache: "pnpm"
- name: Install dependencies
Expand Down
48 changes: 48 additions & 0 deletions .github/release-drafter.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name-template: "Release v$RESOLVED_VERSION"
tag-template: "v$RESOLVED_VERSION"
categories:
- title: "🚀 Features"
label: "feature"
- title: "🐛 Bug Fixes"
label: "bug"
- title: "♻️ Refactor"
label: "refactor"
- title: "📝 Documentation"
label: "documentation"
- title: "🧰 Maintenance"
labels:
- "chore"
- "dependencies"
change-template: "- $TITLE @$AUTHOR (#$NUMBER)"
change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks.
version-resolver:
major:
labels:
- "major"
minor:
labels:
- "minor"
patch:
labels:
- "patch"
default: patch
template: |
## Changes
$CHANGES
autolabeler:
- label: feature
branch:
- "/^feat(ure)?[/-].+/"
- label: bug
branch:
- "/^fix[/-].+/"
- label: refactor
branch:
- "/(refactor|refactoring)[/-].+/"
- label: documentation
branch:
- "/doc(s|umentation)[/-].+/"
- label: chore
branch:
- "/^chore[/-].+/"
45 changes: 42 additions & 3 deletions .github/workflows/example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,57 @@ jobs:
run: pnpm dlx jest --shard=${{ matrix.shard }}
working-directory: examples/default-jest

use-tenbin-test:
use-tenbin-jest:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
shard: [1/3, 2/3, 3/3]
shardIndex: [1, 2, 3]
shardTotal: [3]
directory:
- examples/basic-jest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup
- name: Run build
run: pnpm run build
- name: Restore tenbin-report.json
id: tenbin-report-cache
uses: actions/cache/restore@v4
with:
path: tenbin-report.json
key: tenbin-report
restore-keys: |
tenbin-report-*
- name: Copy tenbin-report.json to working directory
run: |
if [ -e ./tenbin-report.json ]; then
jq . tenbin-report.json
cp tenbin-report.json ${{ matrix.directory }}
fi
- name: Run test
run: pnpm dlx jest --shard=${{ matrix.shard }}
run: pnpm dlx jest --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
working-directory: ${{ matrix.directory }}
- name: Upload tenbin-report.json
if: github.ref_name == 'main'
uses: actions/upload-artifact@v4
with:
name: tenbin-report-${{ matrix.shardIndex }}
path: ${{ matrix.directory }}/tenbin-report.json

merge-and-cache-test-duration:
if: github.ref_name == 'main'
runs-on: ubuntu-latest
needs: [use-tenbin-jest]
steps:
- uses: actions/download-artifact@v4
with:
path: tenbin-report
pattern: tenbin-report-*
- name: Merge tenbin-report
run: jq -s add tenbin-report/**/tenbin-report.json > tenbin-report.json
- name: Cache tenbin-report.json
uses: actions/cache/save@v4
with:
path: tenbin-report.json
key: tenbin-report-${{ github.run_id }}
19 changes: 19 additions & 0 deletions .github/workflows/release-drafter.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: release drafter

on:
push:
branches:
- main
pull_request:
types: [opened, reopened, synchronize]

jobs:
update_release_draft:
permissions:
contents: write
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: release-drafter/release-drafter@v6
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
23 changes: 23 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: npm publish

on:
release:
types: [published]

jobs:
publish:
runs-on: ubuntu-latest
permissions:
id-token: write
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup
- name: Run lint
run: pnpm run lint
- name: Run build
run: pnpm run build
- name: Publish
run: ./scripts/release.sh
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_CONFIG_PROVENANCE: true
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,5 @@ dist
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

tenbin-report.json
1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"directory": "packages/jest-tenbin-sequencer"
},
"license": "MIT",
"types": "dist/index.d.ts",
"exports": {
".": "./dist/index.js",
"./package.json": "./package.json"
Expand Down
41 changes: 37 additions & 4 deletions packages/core/src/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,39 @@
import { test } from "vitest";
import { solver } from "./index";
import { describe, expect, test } from "vitest";
import { partition } from "./index";

test("test", () => {
solver();
const range = (start: number, end: number) => {
const array: number[] = [];
for (let i = start; i <= end; i++) {
array.push(i);
}
return array;
};

describe("partition", () => {
test("number array", () => {
expect(partition([4, 5, 6, 7, 8], 2)).toEqual([
[6, 8],
[4, 5, 7],
]);
expect(partition([4, 5, 6, 7, 8], 3)).toEqual([[8], [4, 7], [5, 6]]);
expect(partition([5, 6, 4, 8, 7], 3)).toEqual([[8], [4, 7], [5, 6]]);
expect(partition(range(10, 30), 3)).toEqual([
[10, 15, 16, 21, 22, 27, 28], // = 129
[11, 14, 17, 20, 23, 26, 29], // = 130
[12, 13, 18, 19, 24, 25, 30], // = 131
]);
});

test("object array", () => {
expect(
partition(
[{ value: 4 }, { value: 5 }, { value: 6 }, { value: 7 }, { value: 8 }],
2,
(item) => item.value,
),
).toEqual([
[{ value: 6 }, { value: 8 }],
[{ value: 4 }, { value: 5 }, { value: 7 }],
]);
});
});
106 changes: 105 additions & 1 deletion packages/core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,105 @@
export const solver = () => {};
type Partition<T> = {
partition: T[][];
diff: number;
};

/**
* Partition data into k partitions such that the difference between the sum of the values of each partition is minimized.
* see: https://en.wikipedia.org/wiki/Largest_differencing_method
*/
export function partition<T>(
data: T[],
k: number,
keyFunc: (item: T) => number = (item) => Number(item),
): T[][] {
if (k === 1) {
throw new Error("k must be greater than 1");
}

let partitions: Partition<T>[] = Array.from(
{ length: data.length },
(_, i) => {
return {
partition: Array.from({ length: k }, (_, k) =>
k === 0 ? [data[i]] : [],
),
diff: keyFunc(data[i]),
};
},
);

while (partitions.length > 1) {
partitions = sortBy(partitions, (item) => item.diff, "asc");
const partitionA = partitions.pop();
const partitionB = partitions.pop();
if (!partitionA || !partitionB) {
throw new Error("partitionA or partitionB should not be undefined");
}
const sortedPartitionA = sortBy<T[]>(
partitionA.partition,
(item: T[]) => sum(item, keyFunc),
"asc",
);
const sortedPartitionB = sortBy<T[]>(
partitionB.partition,
(item: T[]) => sum(item, keyFunc),
"desc",
);
const newPartition = [];
for (let i = 0; i < k; i++) {
newPartition.push([...sortedPartitionA[i], ...sortedPartitionB[i]]);
}
partitions.push({
partition: newPartition,
diff: diff(newPartition.map((item) => sum(item, keyFunc))),
});
}
return partitions[0].partition.map((item) => sortBy(item, keyFunc));
}

function sum<T>(
array: T[],
keyFunc: (item: T) => number = (item) => Number(item),
) {
let sum = 0;
for (let i = 0; i < array.length; i++) {
sum += keyFunc(array[i]);
}
return sum;
}

const diff = (array: number[]) => {
return max(array) - min(array);
};

const max = (array: number[]) => {
let max = array[0];
for (let i = 1; i < array.length; i++) {
if (array[i] > max) {
max = array[i];
}
}
return max;
};

const min = (array: number[]) => {
let min = array[0];
for (let i = 1; i < array.length; i++) {
if (array[i] < min) {
min = array[i];
}
}
return min;
};

const sortBy = <T>(
array: T[],
keyFunc: (item: T) => number = (item) => Number(item),
order: "asc" | "desc" = "asc",
) => {
return array.sort((a, b) => {
const aKey = keyFunc(a);
const bKey = keyFunc(b);
return order === "asc" ? aKey - bKey : bKey - aKey;
});
};
4 changes: 3 additions & 1 deletion packages/core/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
"target": "ES2017",
"moduleResolution": "node",
"skipLibCheck": true,
"declaration": true,
"outDir": "dist"
},
"include": ["src"]
"include": ["src/**/*.ts"],
"exclude": ["src/**/*.test.ts"]
}
3 changes: 2 additions & 1 deletion packages/jest/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"build": "tsc"
},
"dependencies": {
"@jest/test-sequencer": "^29.7.0"
"@jest/test-sequencer": "^29.7.0",
"@tenbin/core": "workspace:*"
},
"devDependencies": {
"@jest/reporters": "29.7.0",
Expand Down
40 changes: 37 additions & 3 deletions packages/jest/src/reporter.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,41 @@
import type { Reporter } from "@jest/reporters";
import * as fs from "node:fs";
import * as path from "node:path";
import type {
AggregatedResult,
Reporter,
Test,
TestResult,
} from "@jest/reporters";

const FILENAME = "tenbin-report.json";

export default class TenbinReporter implements Reporter {
onRunComplete() {
console.log("TenbinReporter.onRunComplete");
private durations: Record<string, number> = {};

onTestResult(test: Test, testResult: TestResult): void {
const relativePath = path.relative(process.cwd(), test.path);
this.durations[relativePath] = this.getDuration(test, testResult) / 1000;
}

onRunComplete(_: unknown, results: AggregatedResult): void {
try {
fs.writeFileSync(
path.join(process.cwd(), FILENAME),
JSON.stringify(this.durations),
);
console.log(
`Test durations written to ${path.join(process.cwd(), FILENAME)}`,
);
} catch (err) {
console.error(err);
}
}

private getDuration(test: Test, testResult: TestResult): number {
if (test.duration !== undefined) {
return test.duration;
}
const { start, end } = testResult.perfStats;
return start && end ? end - start : 0;
}
}
Loading

0 comments on commit 7e3959d

Please sign in to comment.