mirror of
https://github.com/orangecoding/fredy.git
synced 2026-06-16 12:31:07 +00:00
moving from jest to vitest
This commit is contained in:
@@ -25,12 +25,15 @@ export default [
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
...globals.mocha,
|
||||
...globals.jest,
|
||||
Promise: 'readonly',
|
||||
fetch: 'readonly',
|
||||
describe: 'readonly',
|
||||
after: 'readonly',
|
||||
it: 'readonly',
|
||||
beforeEach: 'readonly',
|
||||
afterEach: 'readonly',
|
||||
vi: 'readonly',
|
||||
},
|
||||
},
|
||||
plugins: { react },
|
||||
|
||||
16
package.json
16
package.json
@@ -11,8 +11,8 @@
|
||||
"build:frontend": "vite build",
|
||||
"format": "prettier --write \"**/*.js\"",
|
||||
"format:check": "prettier --check \"**/*.js\"",
|
||||
"test": "node --import ./test/esmock-loader.mjs ./node_modules/mocha/bin/mocha.js --timeout 60000 test/**/*.test.js",
|
||||
"testGH": "node --import ./test/esmock-loader.mjs ./node_modules/mocha/bin/mocha.js --timeout 60000 --exclude test/provider/immonet.test.js --exclude test/provider/immobilienDe.test.js --exclude test/provider/immowelt.test.js test/**/*.test.js",
|
||||
"test": "vitest run",
|
||||
"testGH": "vitest run --config vitest.gh.config.js",
|
||||
"lint": "eslint .",
|
||||
"mcp:stdio": "node lib/mcp/stdio.js",
|
||||
"lint:fix": "yarn lint --fix",
|
||||
@@ -65,15 +65,15 @@
|
||||
"@douyinfe/semi-ui": "2.93.0",
|
||||
"@douyinfe/semi-ui-19": "^2.93.0",
|
||||
"@mapbox/mapbox-gl-draw": "^1.5.1",
|
||||
"@sendgrid/mail": "8.1.6",
|
||||
"@vitejs/plugin-react": "6.0.1",
|
||||
"@modelcontextprotocol/sdk": "^1.27.1",
|
||||
"@sendgrid/mail": "8.1.6",
|
||||
"@turf/boolean-point-in-polygon": "^7.3.4",
|
||||
"@vitejs/plugin-react": "6.0.1",
|
||||
"adm-zip": "^0.5.16",
|
||||
"better-sqlite3": "^12.8.0",
|
||||
"body-parser": "2.2.2",
|
||||
"chart.js": "^4.5.1",
|
||||
"cheerio": "^1.2.0",
|
||||
"@turf/boolean-point-in-polygon": "^7.3.4",
|
||||
"cookie-session": "2.1.1",
|
||||
"handlebars": "4.7.8",
|
||||
"lodash": "4.17.23",
|
||||
@@ -109,19 +109,17 @@
|
||||
"@babel/preset-env": "7.29.0",
|
||||
"@babel/preset-react": "7.28.5",
|
||||
"@eslint/js": "^10.0.1",
|
||||
"chai": "6.2.2",
|
||||
"chalk": "^5.6.2",
|
||||
"eslint": "10.0.3",
|
||||
"eslint-config-prettier": "10.1.8",
|
||||
"eslint-plugin-react": "7.37.5",
|
||||
"esmock": "2.7.3",
|
||||
"globals": "^17.4.0",
|
||||
"history": "5.3.0",
|
||||
"husky": "9.1.7",
|
||||
"less": "4.6.4",
|
||||
"lint-staged": "16.4.0",
|
||||
"mocha": "11.7.5",
|
||||
"nodemon": "^3.1.14",
|
||||
"prettier": "3.8.1"
|
||||
"prettier": "3.8.1",
|
||||
"vitest": "^3.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
* Licensed under Apache-2.0 with Commons Clause and Attribution/Naming Clause
|
||||
*/
|
||||
|
||||
import { expect } from 'chai';
|
||||
import esmock from 'esmock';
|
||||
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
||||
|
||||
describe('services/storage/backupRestoreService.js - precheck & filename', () => {
|
||||
let svc;
|
||||
@@ -14,7 +13,7 @@ describe('services/storage/backupRestoreService.js - precheck & filename', () =>
|
||||
beforeEach(async () => {
|
||||
calls = { logger: { info: [], warn: [], error: [] } };
|
||||
|
||||
// Mock AdmZip with configurable state via globalThis (avoid esmock export name pitfalls)
|
||||
// Mock AdmZip with configurable state via globalThis (avoid mock export name pitfalls)
|
||||
globalThis.__ADM_ZIP_STATE__ = { hasDb: false, meta: null };
|
||||
setZipState = (s) => {
|
||||
globalThis.__ADM_ZIP_STATE__ = { ...globalThis.__ADM_ZIP_STATE__, ...s };
|
||||
@@ -77,67 +76,61 @@ describe('services/storage/backupRestoreService.js - precheck & filename', () =>
|
||||
|
||||
const utilsMock = { getPackageVersion: async () => '16.2.0' };
|
||||
|
||||
const admZipPath = path.join(ROOT, 'node_modules', 'adm-zip', 'adm-zip.js');
|
||||
const mod = await esmock(
|
||||
path.join(ROOT, 'lib', 'services', 'storage', 'backupRestoreService.js'),
|
||||
{},
|
||||
{
|
||||
'adm-zip': admZipMock,
|
||||
[admZipPath]: admZipMock,
|
||||
[migratePath]: migrateMock,
|
||||
[sqlitePath]: sqliteMock,
|
||||
[loggerPath]: loggerMock,
|
||||
[utilsPath]: utilsMock,
|
||||
},
|
||||
);
|
||||
vi.resetModules();
|
||||
vi.doMock('adm-zip', () => admZipMock);
|
||||
vi.doMock(migratePath, () => migrateMock);
|
||||
vi.doMock(sqlitePath, () => sqliteMock);
|
||||
vi.doMock(loggerPath, () => loggerMock);
|
||||
vi.doMock(utilsPath, () => utilsMock);
|
||||
|
||||
const mod = await import(path.join(ROOT, 'lib', 'services', 'storage', 'backupRestoreService.js'));
|
||||
svc = mod;
|
||||
});
|
||||
|
||||
it('precheck: empty upload yields danger', async () => {
|
||||
const res = await svc.precheckRestore(Buffer.alloc(0));
|
||||
expect(res.compatible).to.equal(false);
|
||||
expect(res.severity).to.equal('danger');
|
||||
expect(res.message).to.contain('Empty upload');
|
||||
expect(res.requiredMigration).to.equal(10);
|
||||
expect(res.compatible).toBe(false);
|
||||
expect(res.severity).toBe('danger');
|
||||
expect(res.message).toContain('Empty upload');
|
||||
expect(res.requiredMigration).toBe(10);
|
||||
});
|
||||
|
||||
it('precheck: missing listings.db yields danger', async () => {
|
||||
setZipState({ hasDb: false, meta: { dbMigration: 9 } });
|
||||
const res = await svc.precheckRestore(Buffer.from('dummy'));
|
||||
expect(res.compatible).to.equal(false);
|
||||
expect(res.severity).to.equal('danger');
|
||||
expect(res.message).to.match(/missing the database file/i);
|
||||
expect(res.compatible).toBe(false);
|
||||
expect(res.severity).toBe('danger');
|
||||
expect(res.message).toMatch(/missing the database file/i);
|
||||
});
|
||||
|
||||
it('precheck: older backup is compatible with warning', async () => {
|
||||
setZipState({ hasDb: true, meta: { dbMigration: 5, fredyVersion: '16.0.0' } });
|
||||
const res = await svc.precheckRestore(Buffer.from('zip'));
|
||||
expect(res.compatible).to.equal(true);
|
||||
expect(res.severity).to.equal('warning');
|
||||
expect(res.message).to.match(/automatic migrations/i);
|
||||
expect(res.backupMigration).to.equal(5);
|
||||
expect(res.requiredMigration).to.equal(10);
|
||||
expect(res.compatible).toBe(true);
|
||||
expect(res.severity).toBe('warning');
|
||||
expect(res.message).toMatch(/automatic migrations/i);
|
||||
expect(res.backupMigration).toBe(5);
|
||||
expect(res.requiredMigration).toBe(10);
|
||||
});
|
||||
|
||||
it('precheck: equal backup is compatible with info', async () => {
|
||||
setZipState({ hasDb: true, meta: { dbMigration: 10 } });
|
||||
const res = await svc.precheckRestore(Buffer.from('zip'));
|
||||
expect(res.compatible).to.equal(true);
|
||||
expect(res.severity).to.equal('info');
|
||||
expect(res.compatible).toBe(true);
|
||||
expect(res.severity).toBe('info');
|
||||
});
|
||||
|
||||
it('precheck: newer backup yields danger', async () => {
|
||||
setZipState({ hasDb: true, meta: { dbMigration: 11 } });
|
||||
const res = await svc.precheckRestore(Buffer.from('zip'));
|
||||
expect(res.compatible).to.equal(false);
|
||||
expect(res.severity).to.equal('danger');
|
||||
expect(res.compatible).toBe(false);
|
||||
expect(res.severity).toBe('danger');
|
||||
});
|
||||
|
||||
it('buildBackupFileName: matches pattern and includes version', async () => {
|
||||
const name = await svc.buildBackupFileName();
|
||||
expect(name).to.match(/^\d{4}-\d{2}-\d{2}-FredyBackup-/);
|
||||
expect(name).to.include('16.2.0');
|
||||
expect(name).to.match(/\.zip$/);
|
||||
expect(name).toMatch(/^\d{4}-\d{2}-\d{2}-FredyBackup-/);
|
||||
expect(name).toContain('16.2.0');
|
||||
expect(name).toMatch(/\.zip$/);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
* Licensed under Apache-2.0 with Commons Clause and Attribution/Naming Clause
|
||||
*/
|
||||
|
||||
import { expect } from 'chai';
|
||||
import esmock from 'esmock';
|
||||
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
|
||||
// We will fully mock fs, crypto, SqliteConnection, and dynamic import of migration modules
|
||||
|
||||
@@ -85,22 +84,18 @@ describe('db/migrations/migrate.js - runMigrations', () => {
|
||||
},
|
||||
};
|
||||
|
||||
// esmock with dependency replacements
|
||||
const path = await import('node:path');
|
||||
const ROOT = path.resolve('.');
|
||||
const sqlPath = path.join(ROOT, 'lib', 'services', 'storage', 'SqliteConnection.js');
|
||||
const loggerPath = path.join(ROOT, 'lib', 'services', 'logger.js');
|
||||
const mod = await esmock(
|
||||
'../../../db/migrations/migrate.js',
|
||||
{},
|
||||
{
|
||||
fs: fsMock,
|
||||
crypto: cryptoMock,
|
||||
[sqlPath]: sqlMock,
|
||||
[loggerPath]: loggerMock,
|
||||
},
|
||||
);
|
||||
|
||||
vi.resetModules();
|
||||
vi.doMock('fs', () => ({ default: fsMock, ...fsMock }));
|
||||
vi.doMock('crypto', () => ({ default: cryptoMock, ...cryptoMock }));
|
||||
vi.doMock(sqlPath, () => ({ default: sqlMock }));
|
||||
vi.doMock(loggerPath, () => ({ default: loggerMock }));
|
||||
|
||||
const mod = await import('../../../lib/services/storage/migrations/migrate.js');
|
||||
runMigrations = mod.runMigrations;
|
||||
|
||||
// remember original exitCode to restore later
|
||||
@@ -114,9 +109,9 @@ describe('db/migrations/migrate.js - runMigrations', () => {
|
||||
|
||||
it('logs and returns when no migration files are found', async () => {
|
||||
await runMigrations();
|
||||
expect(calls.logs.info.some((a) => String(a[0]).includes('No migration files'))).to.equal(true);
|
||||
expect(calls.sql.getConnection).to.equal(0);
|
||||
expect(calls.sql.optimize).to.equal(0);
|
||||
expect(calls.logs.info.some((a) => String(a[0]).includes('No migration files'))).toBe(true);
|
||||
expect(calls.sql.getConnection).toBe(0);
|
||||
expect(calls.sql.optimize).toBe(0);
|
||||
});
|
||||
|
||||
it('applies a single new migration inside a transaction and records it', async () => {
|
||||
@@ -165,11 +160,6 @@ describe('db/migrations/migrate.js - runMigrations', () => {
|
||||
},
|
||||
};
|
||||
|
||||
// We need to intercept dynamic import by esmock: provide a stub for import(url)
|
||||
// esmock supports mocking via a virtual module using URL matching, but simpler approach:
|
||||
// place the file path that migrate.js will compute and make Node import resolve to our stub
|
||||
// We simulate by mocking url.pathToFileURL is still used, but dynamic import will be handled by esmock when we map the computed path.
|
||||
|
||||
const path = await import('node:path');
|
||||
const ROOT = path.resolve('.');
|
||||
|
||||
@@ -178,26 +168,22 @@ describe('db/migrations/migrate.js - runMigrations', () => {
|
||||
// Use global importer hook to bypass dynamic import
|
||||
globalThis.__TEST_MIGRATE_IMPORT__ = async () => migrationModule;
|
||||
|
||||
const mod = await esmock(
|
||||
'../../../db/migrations/migrate.js',
|
||||
{},
|
||||
{
|
||||
fs: fsMock,
|
||||
crypto: cryptoMock,
|
||||
[sqlPath]: sqlMock,
|
||||
[loggerPath]: loggerMock,
|
||||
},
|
||||
);
|
||||
vi.resetModules();
|
||||
vi.doMock('fs', () => ({ default: fsMock, ...fsMock }));
|
||||
vi.doMock('crypto', () => ({ default: cryptoMock, ...cryptoMock }));
|
||||
vi.doMock(sqlPath, () => ({ default: sqlMock }));
|
||||
vi.doMock(loggerPath, () => ({ default: loggerMock }));
|
||||
|
||||
const mod = await import('../../../lib/services/storage/migrations/migrate.js');
|
||||
runMigrations = mod.runMigrations;
|
||||
|
||||
await runMigrations();
|
||||
|
||||
// Should have started a transaction and inserted into schema_migrations
|
||||
expect(calls.sql.withTransaction.length).to.equal(1);
|
||||
expect(calls.sql.withTransaction.length).toBe(1);
|
||||
const inserted = calls.sql.execute.find((e) => String(e.sql).includes('INSERT INTO schema_migrations'));
|
||||
expect(!!inserted).to.equal(true);
|
||||
expect(calls.sql.optimize).to.equal(1);
|
||||
expect(!!inserted).toBe(true);
|
||||
expect(calls.sql.optimize).toBe(1);
|
||||
});
|
||||
|
||||
it('skips already executed migration with same checksum', async () => {
|
||||
@@ -242,24 +228,20 @@ describe('db/migrations/migrate.js - runMigrations', () => {
|
||||
|
||||
globalThis.__TEST_MIGRATE_IMPORT__ = async () => ({ up: () => {} });
|
||||
|
||||
const mod = await esmock(
|
||||
'../../../db/migrations/migrate.js',
|
||||
{},
|
||||
{
|
||||
fs: fsMock,
|
||||
crypto: cryptoMock,
|
||||
[sqlPath]: sqlMock,
|
||||
[loggerPath]: loggerMock,
|
||||
},
|
||||
);
|
||||
vi.resetModules();
|
||||
vi.doMock('fs', () => ({ default: fsMock, ...fsMock }));
|
||||
vi.doMock('crypto', () => ({ default: cryptoMock, ...cryptoMock }));
|
||||
vi.doMock(sqlPath, () => ({ default: sqlMock }));
|
||||
vi.doMock(loggerPath, () => ({ default: loggerMock }));
|
||||
|
||||
const mod = await import('../../../lib/services/storage/migrations/migrate.js');
|
||||
runMigrations = mod.runMigrations;
|
||||
|
||||
await runMigrations();
|
||||
|
||||
// Should not run transaction because it's skipped
|
||||
expect(calls.sql.withTransaction.length).to.equal(0);
|
||||
expect(calls.sql.optimize).to.equal(1);
|
||||
expect(calls.sql.withTransaction.length).toBe(0);
|
||||
expect(calls.sql.optimize).toBe(1);
|
||||
});
|
||||
|
||||
it('aborts with exitCode=1 when a migration throws, without applying insert', async () => {
|
||||
@@ -311,24 +293,20 @@ describe('db/migrations/migrate.js - runMigrations', () => {
|
||||
const sqlPath = path.join(ROOT, 'lib', 'services', 'storage', 'SqliteConnection.js');
|
||||
const loggerPath = path.join(ROOT, 'lib', 'services', 'logger.js');
|
||||
|
||||
const mod = await esmock(
|
||||
'../../../lib/services/storage/migrations/migrate.js',
|
||||
{},
|
||||
{
|
||||
fs: fsMock,
|
||||
crypto: cryptoMock,
|
||||
[sqlPath]: sqlMock,
|
||||
[loggerPath]: loggerMock,
|
||||
},
|
||||
);
|
||||
vi.resetModules();
|
||||
vi.doMock('fs', () => ({ default: fsMock, ...fsMock }));
|
||||
vi.doMock('crypto', () => ({ default: cryptoMock, ...cryptoMock }));
|
||||
vi.doMock(sqlPath, () => ({ default: sqlMock }));
|
||||
vi.doMock(loggerPath, () => ({ default: loggerMock }));
|
||||
|
||||
const mod = await import('../../../lib/services/storage/migrations/migrate.js');
|
||||
runMigrations = mod.runMigrations;
|
||||
|
||||
await runMigrations();
|
||||
|
||||
expect(process.exitCode).to.equal(1);
|
||||
expect(process.exitCode).toBe(1);
|
||||
// No insert into schema_migrations should be recorded since transaction failed
|
||||
const inserted = calls.sql.execute.find((e) => String(e.sql).includes('INSERT INTO schema_migrations'));
|
||||
expect(inserted).to.equal(undefined);
|
||||
expect(inserted).toBe(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1 @@
|
||||
import { register } from 'node:module';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
|
||||
register('esmock', pathToFileURL('./'));
|
||||
// No longer needed - using vitest
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under Apache-2.0 with Commons Clause and Attribution/Naming Clause
|
||||
*/
|
||||
|
||||
import { expect } from 'chai';
|
||||
import { expect } from 'vitest';
|
||||
import { mockFredy } from './utils.js';
|
||||
import * as mockStore from './mocks/mockStore.js';
|
||||
|
||||
@@ -34,7 +34,7 @@ describe('Issue reproduction: listings filtered by similarity or area should be
|
||||
// Might throw NoNewListingsWarning if all are filtered out
|
||||
}
|
||||
|
||||
expect(mockStore.deletedIds).to.include('1');
|
||||
expect(mockStore.deletedIds).toContain('1');
|
||||
});
|
||||
|
||||
it('should call deleteListingsById when listings are filtered by area', async () => {
|
||||
@@ -84,6 +84,6 @@ describe('Issue reproduction: listings filtered by similarity or area should be
|
||||
// Might throw NoNewListingsWarning if all are filtered out
|
||||
}
|
||||
|
||||
expect(mockStore.deletedIds).to.include('2');
|
||||
expect(mockStore.deletedIds).toContain('2');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import * as similarityCache from '../../lib/services/similarity-check/similarityCache.js';
|
||||
import { get } from '../mocks/mockNotification.js';
|
||||
import { providerConfig, mockFredy } from '../utils.js';
|
||||
import { expect } from 'chai';
|
||||
import { expect } from 'vitest';
|
||||
import * as provider from '../../lib/provider/einsAImmobilien.js';
|
||||
|
||||
describe('#einsAImmobilien testsuite()', () => {
|
||||
@@ -23,22 +23,22 @@ describe('#einsAImmobilien testsuite()', () => {
|
||||
similarityCache,
|
||||
);
|
||||
fredy.execute().then((listings) => {
|
||||
expect(listings).to.be.a('array');
|
||||
expect(listings).toBeInstanceOf(Array);
|
||||
const notificationObj = get();
|
||||
expect(notificationObj).to.be.a('object');
|
||||
expect(notificationObj.serviceName).to.equal('einsAImmobilien');
|
||||
expect(notificationObj).toBeTypeOf('object');
|
||||
expect(notificationObj.serviceName).toBe('einsAImmobilien');
|
||||
notificationObj.payload.forEach((notify) => {
|
||||
/** check the actual structure **/
|
||||
expect(notify.id).to.be.a('string');
|
||||
expect(notify.price).to.be.a('string');
|
||||
expect(notify.size).to.be.a('string');
|
||||
expect(notify.title).to.be.a('string');
|
||||
expect(notify.link).to.be.a('string');
|
||||
expect(notify.address).to.be.a('string');
|
||||
expect(notify.id).toBeTypeOf('string');
|
||||
expect(notify.price).toBeTypeOf('string');
|
||||
expect(notify.size).toBeTypeOf('string');
|
||||
expect(notify.title).toBeTypeOf('string');
|
||||
expect(notify.link).toBeTypeOf('string');
|
||||
expect(notify.address).toBeTypeOf('string');
|
||||
/** check the values if possible **/
|
||||
expect(notify.size).to.be.not.empty;
|
||||
expect(notify.title).to.be.not.empty;
|
||||
expect(notify.link).that.does.include('https://www.1a-immobilienmarkt.de');
|
||||
expect(notify.size).not.toBe('');
|
||||
expect(notify.title).not.toBe('');
|
||||
expect(notify.link).toContain('https://www.1a-immobilienmarkt.de');
|
||||
});
|
||||
resolve();
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import * as similarityCache from '../../lib/services/similarity-check/similarityCache.js';
|
||||
import { get } from '../mocks/mockNotification.js';
|
||||
import { providerConfig, mockFredy } from '../utils.js';
|
||||
import { expect } from 'chai';
|
||||
import { expect } from 'vitest';
|
||||
import * as provider from '../../lib/provider/immobilienDe.js';
|
||||
|
||||
describe('#immobilien.de testsuite()', () => {
|
||||
@@ -16,24 +16,24 @@ describe('#immobilien.de testsuite()', () => {
|
||||
return await new Promise((resolve) => {
|
||||
const fredy = new Fredy(provider.config, null, null, provider.metaInformation.id, 'test1', similarityCache);
|
||||
fredy.execute().then((listing) => {
|
||||
expect(listing).to.be.a('array');
|
||||
expect(listing).toBeInstanceOf(Array);
|
||||
const notificationObj = get();
|
||||
expect(notificationObj).to.be.a('object');
|
||||
expect(notificationObj.serviceName).to.equal('immobilienDe');
|
||||
expect(notificationObj).toBeTypeOf('object');
|
||||
expect(notificationObj.serviceName).toBe('immobilienDe');
|
||||
notificationObj.payload.forEach((notify) => {
|
||||
/** check the actual structure **/
|
||||
expect(notify.id).to.be.a('string');
|
||||
expect(notify.price).to.be.a('string');
|
||||
expect(notify.size).to.be.a('string');
|
||||
expect(notify.title).to.be.a('string');
|
||||
expect(notify.link).to.be.a('string');
|
||||
expect(notify.address).to.be.a('string');
|
||||
expect(notify.id).toBeTypeOf('string');
|
||||
expect(notify.price).toBeTypeOf('string');
|
||||
expect(notify.size).toBeTypeOf('string');
|
||||
expect(notify.title).toBeTypeOf('string');
|
||||
expect(notify.link).toBeTypeOf('string');
|
||||
expect(notify.address).toBeTypeOf('string');
|
||||
/** check the values if possible **/
|
||||
expect(notify.price).that.does.include('€');
|
||||
expect(notify.size).that.does.include('m²');
|
||||
expect(notify.title).to.be.not.empty;
|
||||
expect(notify.link).that.does.include('https://www.immobilien.de');
|
||||
expect(notify.address).to.be.not.empty;
|
||||
expect(notify.price).toContain('€');
|
||||
expect(notify.size).toContain('m²');
|
||||
expect(notify.title).not.toBe('');
|
||||
expect(notify.link).toContain('https://www.immobilien.de');
|
||||
expect(notify.address).not.toBe('');
|
||||
});
|
||||
resolve();
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under Apache-2.0 with Commons Clause and Attribution/Naming Clause
|
||||
*/
|
||||
|
||||
import { expect } from 'chai';
|
||||
import { expect } from 'vitest';
|
||||
import * as similarityCache from '../../lib/services/similarity-check/similarityCache.js';
|
||||
import { mockFredy, providerConfig } from '../utils.js';
|
||||
import { get } from '../mocks/mockNotification.js';
|
||||
@@ -16,22 +16,22 @@ describe('#immoscout provider testsuite()', () => {
|
||||
return await new Promise((resolve) => {
|
||||
const fredy = new Fredy(provider.config, null, null, provider.metaInformation.id, '', similarityCache);
|
||||
fredy.execute().then((listings) => {
|
||||
expect(listings).to.be.a('array');
|
||||
expect(listings).toBeInstanceOf(Array);
|
||||
const notificationObj = get();
|
||||
expect(notificationObj).to.be.a('object');
|
||||
expect(notificationObj.serviceName).to.equal('immoscout');
|
||||
expect(notificationObj).toBeTypeOf('object');
|
||||
expect(notificationObj.serviceName).toBe('immoscout');
|
||||
notificationObj.payload.forEach((notify) => {
|
||||
/** check the actual structure **/
|
||||
expect(notify.id).to.be.a('string');
|
||||
expect(notify.price).to.be.a('string');
|
||||
expect(notify.size).to.be.a('string');
|
||||
expect(notify.title).to.be.a('string');
|
||||
expect(notify.link).to.be.a('string');
|
||||
expect(notify.address).to.be.a('string');
|
||||
expect(notify.id).toBeTypeOf('string');
|
||||
expect(notify.price).toBeTypeOf('string');
|
||||
expect(notify.size).toBeTypeOf('string');
|
||||
expect(notify.title).toBeTypeOf('string');
|
||||
expect(notify.link).toBeTypeOf('string');
|
||||
expect(notify.address).toBeTypeOf('string');
|
||||
/** check the values if possible **/
|
||||
expect(notify.size).to.be.not.empty;
|
||||
expect(notify.title).to.be.not.empty;
|
||||
expect(notify.link).that.does.include('https://www.immobilienscout24.de/');
|
||||
expect(notify.size).not.toBe('');
|
||||
expect(notify.title).not.toBe('');
|
||||
expect(notify.link).toContain('https://www.immobilienscout24.de/');
|
||||
});
|
||||
resolve();
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import * as similarityCache from '../../lib/services/similarity-check/similarityCache.js';
|
||||
import { get } from '../mocks/mockNotification.js';
|
||||
import { mockFredy, providerConfig } from '../utils.js';
|
||||
import { expect } from 'chai';
|
||||
import { expect } from 'vitest';
|
||||
import * as provider from '../../lib/provider/immoswp.js';
|
||||
|
||||
describe('#immoswp testsuite()', () => {
|
||||
@@ -16,21 +16,21 @@ describe('#immoswp testsuite()', () => {
|
||||
return await new Promise((resolve) => {
|
||||
const fredy = new Fredy(provider.config, null, null, provider.metaInformation.id, 'immoswp', similarityCache);
|
||||
fredy.execute().then((listing) => {
|
||||
expect(listing).to.be.a('array');
|
||||
expect(listing).toBeInstanceOf(Array);
|
||||
const notificationObj = get();
|
||||
expect(notificationObj).to.be.a('object');
|
||||
expect(notificationObj.serviceName).to.equal('immoswp');
|
||||
expect(notificationObj).toBeTypeOf('object');
|
||||
expect(notificationObj.serviceName).toBe('immoswp');
|
||||
notificationObj.payload.forEach((notify) => {
|
||||
/** check the actual structure **/
|
||||
expect(notify.id).to.be.a('string');
|
||||
expect(notify.price).to.be.a('string');
|
||||
expect(notify.size).to.be.a('string');
|
||||
expect(notify.title).to.be.a('string');
|
||||
expect(notify.link).to.be.a('string');
|
||||
expect(notify.id).toBeTypeOf('string');
|
||||
expect(notify.price).toBeTypeOf('string');
|
||||
expect(notify.size).toBeTypeOf('string');
|
||||
expect(notify.title).toBeTypeOf('string');
|
||||
expect(notify.link).toBeTypeOf('string');
|
||||
/** check the values if possible **/
|
||||
expect(notify.price).that.does.include('€');
|
||||
expect(notify.title).to.be.not.empty;
|
||||
expect(notify.link).that.does.include('https://immo.swp.de');
|
||||
expect(notify.price).toContain('€');
|
||||
expect(notify.title).not.toBe('');
|
||||
expect(notify.link).toContain('https://immo.swp.de');
|
||||
});
|
||||
resolve();
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import * as similarityCache from '../../lib/services/similarity-check/similarityCache.js';
|
||||
import { get } from '../mocks/mockNotification.js';
|
||||
import { mockFredy, providerConfig } from '../utils.js';
|
||||
import { expect } from 'chai';
|
||||
import { expect } from 'vitest';
|
||||
import * as provider from '../../lib/provider/immowelt.js';
|
||||
|
||||
describe('#immowelt testsuite()', () => {
|
||||
@@ -17,24 +17,24 @@ describe('#immowelt testsuite()', () => {
|
||||
const fredy = new Fredy(provider.config, null, null, provider.metaInformation.id, 'immowelt', similarityCache);
|
||||
const listing = await fredy.execute();
|
||||
|
||||
expect(listing).to.be.a('array');
|
||||
expect(listing).toBeInstanceOf(Array);
|
||||
const notificationObj = get();
|
||||
expect(notificationObj).to.be.a('object');
|
||||
expect(notificationObj.serviceName).to.equal('immowelt');
|
||||
expect(notificationObj).toBeTypeOf('object');
|
||||
expect(notificationObj.serviceName).toBe('immowelt');
|
||||
notificationObj.payload.forEach((notify) => {
|
||||
/** check the actual structure **/
|
||||
expect(notify.id).to.be.a('string');
|
||||
expect(notify.price).to.be.a('string');
|
||||
expect(notify.title).to.be.a('string');
|
||||
expect(notify.link).to.be.a('string');
|
||||
expect(notify.address).to.be.a('string');
|
||||
expect(notify.id).toBeTypeOf('string');
|
||||
expect(notify.price).toBeTypeOf('string');
|
||||
expect(notify.title).toBeTypeOf('string');
|
||||
expect(notify.link).toBeTypeOf('string');
|
||||
expect(notify.address).toBeTypeOf('string');
|
||||
/** check the values if possible **/
|
||||
if (notify.size != null && notify.size.trim().toLowerCase() !== 'k.a.') {
|
||||
expect(notify.size).that.does.include('m²');
|
||||
expect(notify.size).toContain('m²');
|
||||
}
|
||||
expect(notify.title).to.be.not.empty;
|
||||
expect(notify.link).that.does.include('https://www.immowelt.de');
|
||||
expect(notify.address).to.be.not.empty;
|
||||
expect(notify.title).not.toBe('');
|
||||
expect(notify.link).toContain('https://www.immowelt.de');
|
||||
expect(notify.address).not.toBe('');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import * as similarityCache from '../../lib/services/similarity-check/similarityCache.js';
|
||||
import { get } from '../mocks/mockNotification.js';
|
||||
import { mockFredy, providerConfig } from '../utils.js';
|
||||
import { expect } from 'chai';
|
||||
import { expect } from 'vitest';
|
||||
import * as provider from '../../lib/provider/kleinanzeigen.js';
|
||||
|
||||
describe('#kleinanzeigen testsuite()', () => {
|
||||
@@ -23,20 +23,20 @@ describe('#kleinanzeigen testsuite()', () => {
|
||||
similarityCache,
|
||||
);
|
||||
fredy.execute().then((listing) => {
|
||||
expect(listing).to.be.a('array');
|
||||
expect(listing).toBeInstanceOf(Array);
|
||||
const notificationObj = get();
|
||||
expect(notificationObj).to.be.a('object');
|
||||
expect(notificationObj.serviceName).to.equal('kleinanzeigen');
|
||||
expect(notificationObj).toBeTypeOf('object');
|
||||
expect(notificationObj.serviceName).toBe('kleinanzeigen');
|
||||
notificationObj.payload.forEach((notify) => {
|
||||
/** check the actual structure **/
|
||||
expect(notify.id).to.be.a('string');
|
||||
expect(notify.title).to.be.a('string');
|
||||
expect(notify.link).to.be.a('string');
|
||||
expect(notify.address).to.be.a('string');
|
||||
expect(notify.id).toBeTypeOf('string');
|
||||
expect(notify.title).toBeTypeOf('string');
|
||||
expect(notify.link).toBeTypeOf('string');
|
||||
expect(notify.address).toBeTypeOf('string');
|
||||
/** check the values if possible **/
|
||||
expect(notify.title).to.be.not.empty;
|
||||
expect(notify.link).that.does.include('https://www.kleinanzeigen.de');
|
||||
expect(notify.address).to.be.not.empty;
|
||||
expect(notify.title).not.toBe('');
|
||||
expect(notify.link).toContain('https://www.kleinanzeigen.de');
|
||||
expect(notify.address).not.toBe('');
|
||||
});
|
||||
resolve();
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import * as similarityCache from '../../lib/services/similarity-check/similarityCache.js';
|
||||
import { get } from '../mocks/mockNotification.js';
|
||||
import { mockFredy, providerConfig } from '../utils.js';
|
||||
import { expect } from 'chai';
|
||||
import { expect } from 'vitest';
|
||||
import * as provider from '../../lib/provider/mcMakler.js';
|
||||
|
||||
describe('#mcMakler testsuite()', () => {
|
||||
@@ -17,22 +17,22 @@ describe('#mcMakler testsuite()', () => {
|
||||
const fredy = new Fredy(provider.config, null, null, provider.metaInformation.id, 'mcMakler', similarityCache);
|
||||
const listing = await fredy.execute();
|
||||
|
||||
expect(listing).to.be.a('array');
|
||||
expect(listing).toBeInstanceOf(Array);
|
||||
const notificationObj = get();
|
||||
expect(notificationObj).to.be.a('object');
|
||||
expect(notificationObj.serviceName).to.equal('mcMakler');
|
||||
expect(notificationObj).toBeTypeOf('object');
|
||||
expect(notificationObj.serviceName).toBe('mcMakler');
|
||||
notificationObj.payload.forEach((notify) => {
|
||||
/** check the actual structure **/
|
||||
expect(notify.id).to.be.a('string');
|
||||
expect(notify.price).to.be.a('string');
|
||||
expect(notify.size).to.be.a('string');
|
||||
expect(notify.title).to.be.a('string');
|
||||
expect(notify.link).to.be.a('string');
|
||||
expect(notify.address).to.be.a('string');
|
||||
expect(notify.id).toBeTypeOf('string');
|
||||
expect(notify.price).toBeTypeOf('string');
|
||||
expect(notify.size).toBeTypeOf('string');
|
||||
expect(notify.title).toBeTypeOf('string');
|
||||
expect(notify.link).toBeTypeOf('string');
|
||||
expect(notify.address).toBeTypeOf('string');
|
||||
/** check the values if possible **/
|
||||
expect(notify.size).that.does.include('m²');
|
||||
expect(notify.title).to.be.not.empty;
|
||||
expect(notify.address).to.be.not.empty;
|
||||
expect(notify.size).toContain('m²');
|
||||
expect(notify.title).not.toBe('');
|
||||
expect(notify.address).not.toBe('');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import * as similarityCache from '../../lib/services/similarity-check/similarityCache.js';
|
||||
import { get } from '../mocks/mockNotification.js';
|
||||
import { mockFredy, providerConfig } from '../utils.js';
|
||||
import { expect } from 'chai';
|
||||
import { expect } from 'vitest';
|
||||
import * as provider from '../../lib/provider/neubauKompass.js';
|
||||
|
||||
describe('#neubauKompass testsuite()', () => {
|
||||
@@ -23,20 +23,20 @@ describe('#neubauKompass testsuite()', () => {
|
||||
similarityCache,
|
||||
);
|
||||
fredy.execute().then((listing) => {
|
||||
expect(listing).to.be.a('array');
|
||||
expect(listing).toBeInstanceOf(Array);
|
||||
const notificationObj = get();
|
||||
expect(notificationObj.serviceName).to.equal('neubauKompass');
|
||||
expect(notificationObj.serviceName).toBe('neubauKompass');
|
||||
notificationObj.payload.forEach((notify) => {
|
||||
expect(notify).to.be.a('object');
|
||||
expect(notify).toBeTypeOf('object');
|
||||
/** check the actual structure **/
|
||||
expect(notify.id).to.be.a('string');
|
||||
expect(notify.title).to.be.a('string');
|
||||
expect(notify.link).to.be.a('string');
|
||||
expect(notify.address).to.be.a('string');
|
||||
expect(notify.id).toBeTypeOf('string');
|
||||
expect(notify.title).toBeTypeOf('string');
|
||||
expect(notify.link).toBeTypeOf('string');
|
||||
expect(notify.address).toBeTypeOf('string');
|
||||
/** check the values if possible **/
|
||||
expect(notify.title).to.be.not.empty;
|
||||
expect(notify.link).that.does.include('https://www.neubaukompass.de');
|
||||
expect(notify.address).to.be.not.empty;
|
||||
expect(notify.title).not.toBe('');
|
||||
expect(notify.link).toContain('https://www.neubaukompass.de');
|
||||
expect(notify.address).not.toBe('');
|
||||
});
|
||||
resolve();
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import * as similarityCache from '../../lib/services/similarity-check/similarityCache.js';
|
||||
import { get } from '../mocks/mockNotification.js';
|
||||
import { mockFredy, providerConfig } from '../utils.js';
|
||||
import { expect } from 'chai';
|
||||
import { expect } from 'vitest';
|
||||
import * as provider from '../../lib/provider/ohneMakler.js';
|
||||
|
||||
describe('#ohneMakler testsuite()', () => {
|
||||
@@ -17,22 +17,22 @@ describe('#ohneMakler testsuite()', () => {
|
||||
const fredy = new Fredy(provider.config, null, null, provider.metaInformation.id, 'ohneMakler', similarityCache);
|
||||
const listing = await fredy.execute();
|
||||
|
||||
expect(listing).to.be.a('array');
|
||||
expect(listing).toBeInstanceOf(Array);
|
||||
const notificationObj = get();
|
||||
expect(notificationObj).to.be.a('object');
|
||||
expect(notificationObj.serviceName).to.equal('ohneMakler');
|
||||
expect(notificationObj).toBeTypeOf('object');
|
||||
expect(notificationObj.serviceName).toBe('ohneMakler');
|
||||
notificationObj.payload.forEach((notify) => {
|
||||
/** check the actual structure **/
|
||||
expect(notify.id).to.be.a('string');
|
||||
expect(notify.price).to.be.a('string');
|
||||
expect(notify.size).to.be.a('string');
|
||||
expect(notify.title).to.be.a('string');
|
||||
expect(notify.link).to.be.a('string');
|
||||
expect(notify.address).to.be.a('string');
|
||||
expect(notify.id).toBeTypeOf('string');
|
||||
expect(notify.price).toBeTypeOf('string');
|
||||
expect(notify.size).toBeTypeOf('string');
|
||||
expect(notify.title).toBeTypeOf('string');
|
||||
expect(notify.link).toBeTypeOf('string');
|
||||
expect(notify.address).toBeTypeOf('string');
|
||||
/** check the values if possible **/
|
||||
expect(notify.size).that.does.include('m²');
|
||||
expect(notify.title).to.be.not.empty;
|
||||
expect(notify.address).to.be.not.empty;
|
||||
expect(notify.size).toContain('m²');
|
||||
expect(notify.title).not.toBe('');
|
||||
expect(notify.address).not.toBe('');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import * as similarityCache from '../../lib/services/similarity-check/similarityCache.js';
|
||||
import { get } from '../mocks/mockNotification.js';
|
||||
import { mockFredy, providerConfig } from '../utils.js';
|
||||
import { expect } from 'chai';
|
||||
import { expect } from 'vitest';
|
||||
import * as provider from '../../lib/provider/regionalimmobilien24.js';
|
||||
|
||||
describe('#regionalimmobilien24 testsuite()', () => {
|
||||
@@ -24,22 +24,22 @@ describe('#regionalimmobilien24 testsuite()', () => {
|
||||
);
|
||||
const listing = await fredy.execute();
|
||||
|
||||
expect(listing).to.be.a('array');
|
||||
expect(listing).toBeInstanceOf(Array);
|
||||
const notificationObj = get();
|
||||
expect(notificationObj).to.be.a('object');
|
||||
expect(notificationObj.serviceName).to.equal('regionalimmobilien24');
|
||||
expect(notificationObj).toBeTypeOf('object');
|
||||
expect(notificationObj.serviceName).toBe('regionalimmobilien24');
|
||||
notificationObj.payload.forEach((notify) => {
|
||||
/** check the actual structure **/
|
||||
expect(notify.id).to.be.a('string');
|
||||
expect(notify.price).to.be.a('string');
|
||||
expect(notify.size).to.be.a('string');
|
||||
expect(notify.title).to.be.a('string');
|
||||
expect(notify.link).to.be.a('string');
|
||||
expect(notify.address).to.be.a('string');
|
||||
expect(notify.id).toBeTypeOf('string');
|
||||
expect(notify.price).toBeTypeOf('string');
|
||||
expect(notify.size).toBeTypeOf('string');
|
||||
expect(notify.title).toBeTypeOf('string');
|
||||
expect(notify.link).toBeTypeOf('string');
|
||||
expect(notify.address).toBeTypeOf('string');
|
||||
/** check the values if possible **/
|
||||
expect(notify.size).that.does.include('m²');
|
||||
expect(notify.title).to.be.not.empty;
|
||||
expect(notify.address).to.be.not.empty;
|
||||
expect(notify.size).toContain('m²');
|
||||
expect(notify.title).not.toBe('');
|
||||
expect(notify.address).not.toBe('');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import * as similarityCache from '../../lib/services/similarity-check/similarityCache.js';
|
||||
import { get } from '../mocks/mockNotification.js';
|
||||
import { mockFredy, providerConfig } from '../utils.js';
|
||||
import { expect } from 'chai';
|
||||
import { expect } from 'vitest';
|
||||
import * as provider from '../../lib/provider/sparkasse.js';
|
||||
|
||||
describe('#sparkasse testsuite()', () => {
|
||||
@@ -17,22 +17,21 @@ describe('#sparkasse testsuite()', () => {
|
||||
const fredy = new Fredy(provider.config, null, null, provider.metaInformation.id, 'sparkasse', similarityCache);
|
||||
const listing = await fredy.execute();
|
||||
|
||||
expect(listing).to.be.a('array');
|
||||
expect(listing).toBeInstanceOf(Array);
|
||||
const notificationObj = get();
|
||||
expect(notificationObj).to.be.a('object');
|
||||
expect(notificationObj.serviceName).to.equal('sparkasse');
|
||||
expect(notificationObj).toBeTypeOf('object');
|
||||
expect(notificationObj.serviceName).toBe('sparkasse');
|
||||
notificationObj.payload.forEach((notify) => {
|
||||
/** check the actual structure **/
|
||||
expect(notify.id).to.be.a('string');
|
||||
expect(notify.price).to.be.a('string');
|
||||
expect(notify.size).to.be.a('string');
|
||||
expect(notify.title).to.be.a('string');
|
||||
expect(notify.link).to.be.a('string');
|
||||
expect(notify.address).to.be.a('string');
|
||||
expect(notify.id).toBeTypeOf('string');
|
||||
expect(notify.price).toBeTypeOf('string');
|
||||
expect(notify.title).toBeTypeOf('string');
|
||||
expect(notify.link).toBeTypeOf('string');
|
||||
expect(notify.address).toBeTypeOf('string');
|
||||
/** check the values if possible **/
|
||||
expect(notify.size).that.does.include('m²');
|
||||
expect(notify.title).to.be.not.empty;
|
||||
expect(notify.address).to.be.not.empty;
|
||||
expect(notify.size).toContain('m²');
|
||||
expect(notify.title).not.toBe('');
|
||||
expect(notify.address).not.toBe('');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
"enabled": true
|
||||
},
|
||||
"sparkasse": {
|
||||
"url": "https://immobilien.sparkasse.de/immobilien/treffer?marketingType=buy&objectType=flat&perimeter=10&usageType=residential&zipCityEstateId=62782__Hamburg",
|
||||
"url": "https://immobilien.sparkasse.de/immobilien/treffer?estateTypeGroupingId=403&marketingType=buy&perimeter=10&usageType=residential&zipCityEstateId=51.22422%2F6.78006%2F0__D%C3%BCsseldorf",
|
||||
"enabled": true
|
||||
},
|
||||
"wgGesucht": {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { isOneOf, duringWorkingHoursOrNotSet } from '../../lib/utils.js';
|
||||
import assert from 'assert';
|
||||
import { expect } from 'chai';
|
||||
import { expect } from 'vitest';
|
||||
|
||||
const fakeWorkingHoursConfig = (from, to) => ({
|
||||
workingHours: {
|
||||
@@ -25,19 +25,19 @@ describe('utils', () => {
|
||||
});
|
||||
describe('#duringWorkingHoursOrNotSet()', () => {
|
||||
it('should be false', () => {
|
||||
expect(duringWorkingHoursOrNotSet(fakeWorkingHoursConfig('12:00', '13:00'), 0)).to.be.false;
|
||||
expect(duringWorkingHoursOrNotSet(fakeWorkingHoursConfig('12:00', '13:00'), 0)).toBe(false);
|
||||
});
|
||||
it('should be true', () => {
|
||||
expect(duringWorkingHoursOrNotSet(fakeWorkingHoursConfig('10:00', '16:00'), 1622026740000)).to.be.true;
|
||||
expect(duringWorkingHoursOrNotSet(fakeWorkingHoursConfig('10:00', '16:00'), 1622026740000)).toBe(true);
|
||||
});
|
||||
it('should be true if nothing set', () => {
|
||||
expect(duringWorkingHoursOrNotSet(fakeWorkingHoursConfig(null, null), 1622026740000)).to.be.true;
|
||||
expect(duringWorkingHoursOrNotSet(fakeWorkingHoursConfig(null, null), 1622026740000)).toBe(true);
|
||||
});
|
||||
it('should be true if only to is set', () => {
|
||||
expect(duringWorkingHoursOrNotSet(fakeWorkingHoursConfig(null, '13:00'), 1622026740000)).to.be.true;
|
||||
expect(duringWorkingHoursOrNotSet(fakeWorkingHoursConfig(null, '13:00'), 1622026740000)).toBe(true);
|
||||
});
|
||||
it('should be true if only from is set', () => {
|
||||
expect(duringWorkingHoursOrNotSet(fakeWorkingHoursConfig('12:00', null), 1622026740000)).to.be.true;
|
||||
expect(duringWorkingHoursOrNotSet(fakeWorkingHoursConfig('12:00', null), 1622026740000)).toBe(true);
|
||||
});
|
||||
it('should handle working hours that cross midnight (e.g., 05:00 → 00:30)', () => {
|
||||
const cfg = fakeWorkingHoursConfig('05:00', '00:30');
|
||||
@@ -49,9 +49,9 @@ describe('utils', () => {
|
||||
d.setMilliseconds(0);
|
||||
return d.getTime();
|
||||
};
|
||||
expect(duringWorkingHoursOrNotSet(cfg, mkTs(23, 0))).to.be.true; // 23:00 => within window
|
||||
expect(duringWorkingHoursOrNotSet(cfg, mkTs(1, 0))).to.be.false; // 01:00 => outside window
|
||||
expect(duringWorkingHoursOrNotSet(cfg, mkTs(6, 0))).to.be.true; // 06:00 => within window
|
||||
expect(duringWorkingHoursOrNotSet(cfg, mkTs(23, 0))).toBe(true); // 23:00 => within window
|
||||
expect(duringWorkingHoursOrNotSet(cfg, mkTs(1, 0))).toBe(false); // 01:00 => outside window
|
||||
expect(duringWorkingHoursOrNotSet(cfg, mkTs(6, 0))).toBe(true); // 06:00 => within window
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import * as similarityCache from '../../lib/services/similarity-check/similarityCache.js';
|
||||
import { get } from '../mocks/mockNotification.js';
|
||||
import { mockFredy, providerConfig } from '../utils.js';
|
||||
import { expect } from 'chai';
|
||||
import { expect } from 'vitest';
|
||||
import * as provider from '../../lib/provider/wgGesucht.js';
|
||||
|
||||
describe('#wgGesucht testsuite()', () => {
|
||||
@@ -16,17 +16,17 @@ describe('#wgGesucht testsuite()', () => {
|
||||
return await new Promise((resolve) => {
|
||||
const fredy = new Fredy(provider.config, null, null, provider.metaInformation.id, 'wgGesucht', similarityCache);
|
||||
fredy.execute().then((listing) => {
|
||||
expect(listing).to.be.a('array');
|
||||
expect(listing).toBeInstanceOf(Array);
|
||||
const notificationObj = get();
|
||||
expect(notificationObj.serviceName).to.equal('wgGesucht');
|
||||
expect(notificationObj.serviceName).toBe('wgGesucht');
|
||||
notificationObj.payload.forEach((notify) => {
|
||||
expect(notify).to.be.a('object');
|
||||
expect(notify).toBeTypeOf('object');
|
||||
/** check the actual structure **/
|
||||
expect(notify.id).to.be.a('string');
|
||||
expect(notify.title).to.be.a('string');
|
||||
expect(notify.details).to.be.a('string');
|
||||
expect(notify.price).to.be.a('string');
|
||||
expect(notify.link).to.be.a('string');
|
||||
expect(notify.id).toBeTypeOf('string');
|
||||
expect(notify.title).toBeTypeOf('string');
|
||||
expect(notify.details).toBeTypeOf('string');
|
||||
expect(notify.price).toBeTypeOf('string');
|
||||
expect(notify.link).toBeTypeOf('string');
|
||||
});
|
||||
resolve();
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import * as similarityCache from '../../lib/services/similarity-check/similarityCache.js';
|
||||
import { get } from '../mocks/mockNotification.js';
|
||||
import { providerConfig, mockFredy } from '../utils.js';
|
||||
import { expect } from 'chai';
|
||||
import { expect } from 'vitest';
|
||||
import * as provider from '../../lib/provider/wohnungsboerse.js';
|
||||
|
||||
describe('#wohnungsboerse testsuite()', () => {
|
||||
@@ -23,22 +23,22 @@ describe('#wohnungsboerse testsuite()', () => {
|
||||
similarityCache,
|
||||
);
|
||||
fredy.execute().then((listings) => {
|
||||
expect(listings).to.be.a('array');
|
||||
expect(listings).toBeInstanceOf(Array);
|
||||
const notificationObj = get();
|
||||
expect(notificationObj).to.be.a('object');
|
||||
expect(notificationObj.serviceName).to.equal('wohnungsboerse');
|
||||
expect(notificationObj).toBeTypeOf('object');
|
||||
expect(notificationObj.serviceName).toBe('wohnungsboerse');
|
||||
notificationObj.payload.forEach((notify) => {
|
||||
/** check the actual structure **/
|
||||
expect(notify.id).to.be.a('string');
|
||||
expect(notify.price).to.be.a('string');
|
||||
expect(notify.size).to.be.a('string');
|
||||
expect(notify.title).to.be.a('string');
|
||||
expect(notify.link).to.be.a('string');
|
||||
expect(notify.address).to.be.a('string');
|
||||
expect(notify.id).toBeTypeOf('string');
|
||||
expect(notify.price).toBeTypeOf('string');
|
||||
expect(notify.size).toBeTypeOf('string');
|
||||
expect(notify.title).toBeTypeOf('string');
|
||||
expect(notify.link).toBeTypeOf('string');
|
||||
expect(notify.address).toBeTypeOf('string');
|
||||
/** check the values if possible **/
|
||||
expect(notify.size).to.be.not.empty;
|
||||
expect(notify.title).to.be.not.empty;
|
||||
expect(notify.link).that.does.include('https://www.wohnungsboerse.net');
|
||||
expect(notify.size).not.toBe('');
|
||||
expect(notify.title).not.toBe('');
|
||||
expect(notify.link).toContain('https://www.wohnungsboerse.net');
|
||||
});
|
||||
resolve();
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import { expect } from 'chai';
|
||||
import { expect } from 'vitest';
|
||||
import { readFile } from 'fs/promises';
|
||||
import mutator from '../../lib/services/queryStringMutator.js';
|
||||
import queryString from 'query-string';
|
||||
@@ -33,8 +33,8 @@ describe('queryStringMutator', () => {
|
||||
const expectedParams = queryString.parseUrl(test.shouldBecome);
|
||||
const actualParams = queryString.parseUrl(fixedUrl);
|
||||
//check if all new params are existing
|
||||
expect(Object.keys(expectedParams.query)).to.include.members(Object.keys(actualParams.query));
|
||||
expect(Object.values(expectedParams.query)).to.include.members(Object.values(actualParams.query));
|
||||
expect(Object.keys(expectedParams.query)).toEqual(expect.arrayContaining(Object.keys(actualParams.query)));
|
||||
expect(Object.values(expectedParams.query)).toEqual(expect.arrayContaining(Object.values(actualParams.query)));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
* Licensed under Apache-2.0 with Commons Clause and Attribution/Naming Clause
|
||||
*/
|
||||
|
||||
import { describe, it } from 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { expect } from 'vitest';
|
||||
|
||||
import {
|
||||
getPreLaunchConfig,
|
||||
@@ -26,16 +25,16 @@ describe('botPrevention helper', () => {
|
||||
};
|
||||
const cfg = getPreLaunchConfig(url, options);
|
||||
|
||||
expect(cfg.acceptLanguage).to.equal('de-DE,de;q=0.9');
|
||||
expect(cfg.langArg).to.equal('--lang=de-DE');
|
||||
expect(cfg.windowSizeArg).to.equal('--window-size=1200,700');
|
||||
expect(cfg.viewport).to.deep.equal({ width: 1200, height: 700, deviceScaleFactor: 2 });
|
||||
expect(cfg.userAgent).to.equal('TestAgent/1.0');
|
||||
expect(cfg.headers['Accept-Language']).to.equal('de-DE,de;q=0.9');
|
||||
expect(cfg.headers['User-Agent']).to.equal('TestAgent/1.0');
|
||||
expect(cfg.headers.Referer).to.equal('https://example.com/ref');
|
||||
expect(cfg.extraArgs).to.include('--disable-blink-features=AutomationControlled');
|
||||
expect(cfg.extraArgs).to.include('--proxy-bypass-list=<-loopback>');
|
||||
expect(cfg.acceptLanguage).toBe('de-DE,de;q=0.9');
|
||||
expect(cfg.langArg).toBe('--lang=de-DE');
|
||||
expect(cfg.windowSizeArg).toBe('--window-size=1200,700');
|
||||
expect(cfg.viewport).toEqual({ width: 1200, height: 700, deviceScaleFactor: 2 });
|
||||
expect(cfg.userAgent).toBe('TestAgent/1.0');
|
||||
expect(cfg.headers['Accept-Language']).toBe('de-DE,de;q=0.9');
|
||||
expect(cfg.headers['User-Agent']).toBe('TestAgent/1.0');
|
||||
expect(cfg.headers.Referer).toBe('https://example.com/ref');
|
||||
expect(cfg.extraArgs).toContain('--disable-blink-features=AutomationControlled');
|
||||
expect(cfg.extraArgs).toContain('--proxy-bypass-list=<-loopback>');
|
||||
});
|
||||
|
||||
it('applyBotPreventionToPage sets UA, viewport, headers and injects patches', async () => {
|
||||
@@ -58,15 +57,15 @@ describe('botPrevention helper', () => {
|
||||
|
||||
await applyBotPreventionToPage(page, cfg);
|
||||
|
||||
expect(calls[0]).to.deep.equal(['setUserAgent', 'Foo/Bar']);
|
||||
expect(calls.some((c) => c[0] === 'setViewport' && c[1].width === 1000 && c[1].height === 600)).to.equal(true);
|
||||
expect(calls.some((c) => c[0] === 'setJavaScriptEnabled' && c[1] === true)).to.equal(true);
|
||||
expect(calls[0]).toEqual(['setUserAgent', 'Foo/Bar']);
|
||||
expect(calls.some((c) => c[0] === 'setViewport' && c[1].width === 1000 && c[1].height === 600)).toBe(true);
|
||||
expect(calls.some((c) => c[0] === 'setJavaScriptEnabled' && c[1] === true)).toBe(true);
|
||||
const headerCall = calls.find((c) => c[0] === 'setExtraHTTPHeaders');
|
||||
expect(headerCall).to.exist;
|
||||
expect(headerCall[1]['Accept-Language']).to.equal('en-US,en');
|
||||
expect(headerCall[1]['User-Agent']).to.equal('Foo/Bar');
|
||||
expect(calls.some((c) => c[0] === 'emulateTimezone' && c[1] === 'UTC')).to.equal(true);
|
||||
expect(calls.some((c) => c[0] === 'evaluateOnNewDocument' && c[1] === 'function')).to.equal(true);
|
||||
expect(headerCall).toBeDefined();
|
||||
expect(headerCall[1]['Accept-Language']).toBe('en-US,en');
|
||||
expect(headerCall[1]['User-Agent']).toBe('Foo/Bar');
|
||||
expect(calls.some((c) => c[0] === 'emulateTimezone' && c[1] === 'UTC')).toBe(true);
|
||||
expect(calls.some((c) => c[0] === 'evaluateOnNewDocument' && c[1] === 'function')).toBe(true);
|
||||
});
|
||||
|
||||
it('applyLanguagePersistence stores languages early', async () => {
|
||||
@@ -80,9 +79,9 @@ describe('botPrevention helper', () => {
|
||||
});
|
||||
await applyLanguagePersistence(page, cfg);
|
||||
const call = calls[0];
|
||||
expect(call[0]).to.equal('evaluateOnNewDocument');
|
||||
expect(call[1]).to.equal('function');
|
||||
expect(call[2]).to.equal('de-DE,de');
|
||||
expect(call[0]).toBe('evaluateOnNewDocument');
|
||||
expect(call[1]).toBe('function');
|
||||
expect(call[2]).toBe('de-DE,de');
|
||||
});
|
||||
|
||||
it('applyPostNavigationHumanSignals moves mouse and scrolls when enabled', async () => {
|
||||
@@ -98,7 +97,7 @@ describe('botPrevention helper', () => {
|
||||
viewport: { width: 1200, height: 800 },
|
||||
};
|
||||
await applyPostNavigationHumanSignals(page, cfg);
|
||||
expect(mouseCalls.some((c) => c[0] === 'move')).to.equal(true);
|
||||
expect(mouseCalls.some((c) => c[0] === 'wheel')).to.equal(true);
|
||||
expect(mouseCalls.some((c) => c[0] === 'move')).toBe(true);
|
||||
expect(mouseCalls.some((c) => c[0] === 'wheel')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import { convertWebToMobile } from '../../../lib/services/immoscout/immoscout-web-translator.js';
|
||||
import { expect } from 'chai';
|
||||
import { expect } from 'vitest';
|
||||
import { readFile } from 'fs/promises';
|
||||
|
||||
export const testData = JSON.parse(await readFile(new URL('./testdata.json', import.meta.url)));
|
||||
@@ -18,7 +18,7 @@ describe('#immoscout-mobile URL conversion', () => {
|
||||
'https://api.mobile.immobilienscout24.de/search/list?apartmenttypes=halfbasement,penthouse,other,loft,groundfloor,terracedflat,raisedgroundfloor,roofstorey,apartment,maisonette&constructionyear=1920-2026&energyefficiencyclasses=a,b,c,d,e,f,g,h,a_plus&equipment=parking,cellar,builtInKitchen,lift,garden,guestToilet,balcony&exclusioncriteria=projectlisting,swapflat&floor=2-7&geocodes=%2Fde%2Fberlin%2Fberlin&haspromotion=false&heatingtypes=central,selfcontainedcentral&livingspace=10.0-25.0&numberofrooms=2.0-5.0&petsallowedtypes=no,yes,negotiable&price=10.0-100.0&pricetype=calculatedtotalrent&realestatetype=apartmentrent&searchType=region';
|
||||
|
||||
const actualMobileUrl = convertWebToMobile(webUrl);
|
||||
expect(actualMobileUrl).to.equal(expectedMobileUrl);
|
||||
expect(actualMobileUrl).toBe(expectedMobileUrl);
|
||||
});
|
||||
|
||||
// Test URL conversion of web-only SEO path
|
||||
@@ -27,27 +27,27 @@ describe('#immoscout-mobile URL conversion', () => {
|
||||
|
||||
const converted = convertWebToMobile(webUrl);
|
||||
const queryParams = new URL(converted).searchParams;
|
||||
expect(queryParams.get('equipment').split(',')).to.include.members(['garden', 'balcony']);
|
||||
expect(queryParams.get('equipment').split(',')).toEqual(expect.arrayContaining(['garden', 'balcony']));
|
||||
});
|
||||
|
||||
// Test URL conversion with unsupported query parameters
|
||||
it('should remove unsupported query parameters', () => {
|
||||
const webUrl = 'https://www.immobilienscout24.de/Suche/de/berlin/berlin/wohnung-mieten?minimuminternetspeed=100000';
|
||||
const converted = convertWebToMobile(webUrl);
|
||||
expect(converted).that.does.not.include('minimuminternetspeed');
|
||||
expect(converted).not.toContain('minimuminternetspeed');
|
||||
});
|
||||
|
||||
// Test URL conversion with invalid URL
|
||||
it('should throw an error for invalid URL', () => {
|
||||
const invalidUrl = 'invalid-url';
|
||||
|
||||
expect(() => convertWebToMobile(invalidUrl)).to.throw('Invalid URL: invalid-url');
|
||||
expect(() => convertWebToMobile(invalidUrl)).toThrow('Invalid URL: invalid-url');
|
||||
});
|
||||
|
||||
// Test URL conversion with unexpected path format
|
||||
it('should throw an error for unexpected path format', () => {
|
||||
const webUrl = 'https://www.immobilienscout24.de/invalid/path/format';
|
||||
expect(() => convertWebToMobile(webUrl)).to.throw('Unexpected path format: /invalid/path/format');
|
||||
expect(() => convertWebToMobile(webUrl)).toThrow('Unexpected path format: /invalid/path/format');
|
||||
});
|
||||
|
||||
it('shouldFindResultsForEveryTestData', async () => {
|
||||
@@ -70,14 +70,12 @@ describe('#immoscout-mobile URL conversion', () => {
|
||||
console.error('Error fetching data from ImmoScout Mobile API:', response.statusText);
|
||||
}
|
||||
|
||||
expect([null, true]).to.include(response.ok);
|
||||
expect([null, true]).toContain(response.ok);
|
||||
const responseBody = await response.json();
|
||||
expect(responseBody.totalResults).to.be.greaterThan(0);
|
||||
expect(responseBody.totalResults).to.be.greaterThan(0);
|
||||
expect(responseBody.resultListItems.length).to.greaterThan(0);
|
||||
expect(responseBody.resultListItems.filter((r) => r.type === 'EXPOSE_RESULT')[0].item.realEstateType).to.equal(
|
||||
type,
|
||||
);
|
||||
expect(responseBody.totalResults).toBeGreaterThan(0);
|
||||
expect(responseBody.totalResults).toBeGreaterThan(0);
|
||||
expect(responseBody.resultListItems.length).toBeGreaterThan(0);
|
||||
expect(responseBody.resultListItems.filter((r) => r.type === 'EXPOSE_RESULT')[0].item.realEstateType).toBe(type);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
* Licensed under Apache-2.0 with Commons Clause and Attribution/Naming Clause
|
||||
*/
|
||||
|
||||
import { expect } from 'chai';
|
||||
import esmock from 'esmock';
|
||||
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
||||
import { EventEmitter } from 'node:events';
|
||||
|
||||
describe('services/jobs/jobExecutionService', () => {
|
||||
@@ -22,45 +21,39 @@ describe('services/jobs/jobExecutionService', () => {
|
||||
const brokerPath = root + '/lib/services/sse/sse-broker.js';
|
||||
const utilsPath = root + '/lib/utils.js';
|
||||
const loggerPath = root + '/lib/services/logger.js';
|
||||
const notifyPath = root + '/lib/notification/notify.js';
|
||||
|
||||
// esmock the service with all its collaborators
|
||||
const mod = await esmock(
|
||||
svcPath,
|
||||
{},
|
||||
{
|
||||
[busPath]: { bus },
|
||||
[jobStoragePath]: {
|
||||
vi.resetModules();
|
||||
vi.doMock(busPath, () => ({ bus }));
|
||||
vi.doMock(jobStoragePath, () => ({
|
||||
getJob: (id) => state.jobsById[id] || null,
|
||||
getJobs: () => state.jobsList.slice(),
|
||||
},
|
||||
[userStoragePath]: {
|
||||
}));
|
||||
vi.doMock(userStoragePath, () => ({
|
||||
getUsers: () => state.users.slice(),
|
||||
getUser: (id) => state.users.find((u) => u.id === id) || null,
|
||||
},
|
||||
[brokerPath]: {
|
||||
}));
|
||||
vi.doMock(brokerPath, () => ({
|
||||
sendToUsers: (...args) => calls.sent.push(args),
|
||||
},
|
||||
[utilsPath]: {
|
||||
duringWorkingHoursOrNotSet: () => false, // avoid startup run
|
||||
},
|
||||
[loggerPath]: {
|
||||
debug: () => {},
|
||||
info: () => {},
|
||||
warn: () => {},
|
||||
error: () => {},
|
||||
},
|
||||
[root + '/lib/services/jobs/run-state.js']: {
|
||||
}));
|
||||
vi.doMock(utilsPath, () => ({
|
||||
duringWorkingHoursOrNotSet: () => false,
|
||||
}));
|
||||
vi.doMock(loggerPath, () => {
|
||||
const m = { debug: () => {}, info: () => {}, warn: () => {}, error: () => {} };
|
||||
return { default: m };
|
||||
});
|
||||
vi.doMock(notifyPath, () => ({ send: async () => [] }));
|
||||
vi.doMock(root + '/lib/services/jobs/run-state.js', () => ({
|
||||
isRunning: () => false,
|
||||
markRunning: (id) => {
|
||||
calls.markRunning.push(id);
|
||||
return true;
|
||||
},
|
||||
markFinished: () => {},
|
||||
},
|
||||
},
|
||||
);
|
||||
}));
|
||||
|
||||
// call initializer with minimal deps
|
||||
const mod = await import(svcPath);
|
||||
mod.initJobExecutionService({ providers: [], settings: { demoMode: false }, intervalMs: 0 });
|
||||
return mod;
|
||||
}
|
||||
@@ -87,13 +80,13 @@ describe('services/jobs/jobExecutionService', () => {
|
||||
|
||||
bus.emit('jobs:status', { jobId: 'j1', running: true });
|
||||
|
||||
expect(calls.sent.length).to.equal(1, 'sendToUsers should be called once');
|
||||
expect(calls.sent.length, 'sendToUsers should be called once').toBe(1);
|
||||
const [recipients, event, data] = calls.sent[0];
|
||||
expect(event).to.equal('jobStatus');
|
||||
expect(data).to.deep.equal({ jobId: 'j1', running: true });
|
||||
expect(event).toBe('jobStatus');
|
||||
expect(data).toEqual({ jobId: 'j1', running: true });
|
||||
const got = new Set(recipients);
|
||||
const expected = new Set(['owner1', 'u2', 'a1']);
|
||||
expect(got).to.deep.equal(expected);
|
||||
expect(got).toEqual(expected);
|
||||
});
|
||||
|
||||
it('runs all jobs for admin; only own jobs for regular user', async () => {
|
||||
@@ -113,12 +106,12 @@ describe('services/jobs/jobExecutionService', () => {
|
||||
bus.emit('jobs:runAll', { userId: 'u1' });
|
||||
// allow microtasks to flush
|
||||
await new Promise((r) => setTimeout(r, 0));
|
||||
expect(new Set(calls.markRunning)).to.deep.equal(new Set(['j1']));
|
||||
expect(new Set(calls.markRunning)).toEqual(new Set(['j1']));
|
||||
|
||||
// Admin: all jobs
|
||||
calls.markRunning = [];
|
||||
bus.emit('jobs:runAll', { userId: 'admin' });
|
||||
await new Promise((r) => setTimeout(r, 0));
|
||||
expect(new Set(calls.markRunning)).to.deep.equal(new Set(['j1', 'j2']));
|
||||
expect(new Set(calls.markRunning)).toEqual(new Set(['j1', 'j2']));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,18 +3,15 @@
|
||||
* Licensed under Apache-2.0 with Commons Clause and Attribution/Naming Clause
|
||||
*/
|
||||
|
||||
import { expect } from 'chai';
|
||||
import esmock from 'esmock';
|
||||
import { vi, describe, it, expect } from 'vitest';
|
||||
|
||||
// Helper to create module under test with mocks
|
||||
async function loadModuleWith({ entries = [] } = {}) {
|
||||
const mod = await esmock('../../lib/services/similarity-check/similarityCache.js', {
|
||||
// Mock the storage to return our controlled entries
|
||||
'../../lib/services/storage/listingsStorage.js': {
|
||||
vi.resetModules();
|
||||
vi.doMock('../../lib/services/storage/listingsStorage.js', () => ({
|
||||
getAllEntriesFromListings: () => entries,
|
||||
},
|
||||
});
|
||||
return mod;
|
||||
}));
|
||||
return await import('../../lib/services/similarity-check/similarityCache.js');
|
||||
}
|
||||
|
||||
describe('similarityCache', () => {
|
||||
@@ -27,15 +24,15 @@ describe('similarityCache', () => {
|
||||
const { initSimilarityCache, checkAndAddEntry } = await loadModuleWith({ entries });
|
||||
|
||||
// Initially, duplicates should not be detected for new data
|
||||
expect(checkAndAddEntry({ title: 'X', price: 200, address: 'Y' })).to.equal(false);
|
||||
expect(checkAndAddEntry({ title: 'X', price: 200, address: 'Y' })).toBe(false);
|
||||
|
||||
// Now initialize from storage
|
||||
initSimilarityCache();
|
||||
|
||||
// Exact duplicates should be detected
|
||||
expect(checkAndAddEntry({ title: 'A', price: 1000, address: 'Main 1' })).to.equal(true);
|
||||
expect(checkAndAddEntry({ title: 'A', price: 1000, address: 'Main 1' })).toBe(true);
|
||||
// Ensure falsy-but-valid price 0 is preserved by hashing and detected as duplicate
|
||||
expect(checkAndAddEntry({ title: 'B', price: 0, address: 'Zero St' })).to.equal(true);
|
||||
expect(checkAndAddEntry({ title: 'B', price: 0, address: 'Zero St' })).toBe(true);
|
||||
});
|
||||
|
||||
it('checkAndAddEntry returns false for new entry then true for duplicate on second call', async () => {
|
||||
@@ -44,8 +41,8 @@ describe('similarityCache', () => {
|
||||
const first = checkAndAddEntry({ title: 'C', price: 300, address: 'Road 3' });
|
||||
const second = checkAndAddEntry({ title: 'C', price: 300, address: 'Road 3' });
|
||||
|
||||
expect(first).to.equal(false);
|
||||
expect(second).to.equal(true);
|
||||
expect(first).toBe(false);
|
||||
expect(second).toBe(true);
|
||||
});
|
||||
|
||||
it('hashing ignores null/undefined but preserves 0 via behavior', async () => {
|
||||
@@ -53,15 +50,15 @@ describe('similarityCache', () => {
|
||||
|
||||
// Add baseline (null address ignored)
|
||||
const add1 = checkAndAddEntry({ title: 'T', price: 1, address: null });
|
||||
expect(add1).to.equal(false);
|
||||
expect(add1).toBe(false);
|
||||
// Duplicate with undefined address should match
|
||||
const dup = checkAndAddEntry({ title: 'T', price: 1, address: undefined });
|
||||
expect(dup).to.equal(true);
|
||||
expect(dup).toBe(true);
|
||||
|
||||
// Now test that price 0 is preserved (not filtered out)
|
||||
const addZero = checkAndAddEntry({ title: 'Z', price: 0, address: 'Zero' });
|
||||
expect(addZero).to.equal(false);
|
||||
expect(addZero).toBe(false);
|
||||
const dupZero = checkAndAddEntry({ title: 'Z', price: 0, address: 'Zero' });
|
||||
expect(dupZero).to.equal(true);
|
||||
expect(dupZero).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
* Licensed under Apache-2.0 with Commons Clause and Attribution/Naming Clause
|
||||
*/
|
||||
|
||||
import { expect } from 'chai';
|
||||
import esmock from 'esmock';
|
||||
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
|
||||
// We explicitly avoid touching the real filesystem or creating a real DB file.
|
||||
// better-sqlite3 is fully mocked and operates in-memory via our stubs.
|
||||
@@ -78,15 +77,10 @@ describe('SqliteConnection', () => {
|
||||
};
|
||||
};
|
||||
|
||||
// esmock the module with our stubs
|
||||
SqliteConnection = await esmock(
|
||||
'../../lib/services/storage/SqliteConnection.js',
|
||||
{},
|
||||
{
|
||||
fs: fsMock,
|
||||
'better-sqlite3': { default: BetterSqlite3Mock },
|
||||
},
|
||||
);
|
||||
vi.resetModules();
|
||||
vi.doMock('fs', () => ({ default: fsMock, ...fsMock }));
|
||||
vi.doMock('better-sqlite3', () => ({ default: BetterSqlite3Mock }));
|
||||
SqliteConnection = (await import('../../lib/services/storage/SqliteConnection.js')).default;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -98,9 +92,9 @@ describe('SqliteConnection', () => {
|
||||
const db1 = SqliteConnection.getConnection();
|
||||
const db2 = SqliteConnection.getConnection();
|
||||
|
||||
expect(db1).to.equal(db2);
|
||||
expect(db1).toBe(db2);
|
||||
// journal_mode, synchronous, cache_size, foreign_keys, optimize
|
||||
expect(calls.db.pragma).to.deep.equal([
|
||||
expect(calls.db.pragma).toEqual([
|
||||
'journal_mode = WAL',
|
||||
'synchronous = NORMAL',
|
||||
'cache_size = -64000',
|
||||
@@ -108,21 +102,21 @@ describe('SqliteConnection', () => {
|
||||
'optimize',
|
||||
]);
|
||||
// mkdirSync should not be called because existsSync returned true
|
||||
expect(calls.fs.mkdirSync).to.have.length(0);
|
||||
expect(calls.fs.mkdirSync).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('executes query and execute helpers', () => {
|
||||
const rows = SqliteConnection.query('SELECT 1', {});
|
||||
expect(rows).to.be.an('array');
|
||||
expect(rows[0]).to.deep.equal({ x: 1 });
|
||||
expect(rows).toBeInstanceOf(Array);
|
||||
expect(rows[0]).toEqual({ x: 1 });
|
||||
|
||||
const info = SqliteConnection.execute('UPDATE x SET y=1 WHERE id=@id', { id: 5 });
|
||||
expect(info).to.have.property('changes', 1);
|
||||
expect(info).toHaveProperty('changes', 1);
|
||||
});
|
||||
|
||||
it('tableExists uses sqlite_master get()', () => {
|
||||
const exists = SqliteConnection.tableExists('users');
|
||||
expect(exists).to.equal(true);
|
||||
expect(exists).toBe(true);
|
||||
});
|
||||
|
||||
it('withTransaction wraps callback', () => {
|
||||
@@ -131,17 +125,17 @@ describe('SqliteConnection', () => {
|
||||
db.prepare('SELECT inside').all({});
|
||||
return 42;
|
||||
});
|
||||
expect(result).to.equal(42);
|
||||
expect(calls.db.prepare).to.include('SELECT inside');
|
||||
expect(result).toBe(42);
|
||||
expect(calls.db.prepare).toContain('SELECT inside');
|
||||
});
|
||||
|
||||
it('optimize() delegates to PRAGMA optimize and close() calls it again then closes', () => {
|
||||
SqliteConnection.optimize();
|
||||
// It will use the existing connection and call pragma('optimize')
|
||||
expect(calls.db.pragma).to.include('optimize');
|
||||
expect(calls.db.pragma).toContain('optimize');
|
||||
|
||||
SqliteConnection.close();
|
||||
// close increments close counter
|
||||
expect(calls.db.close).to.equal(1);
|
||||
expect(calls.db.close).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,29 +3,24 @@
|
||||
* Licensed under Apache-2.0 with Commons Clause and Attribution/Naming Clause
|
||||
*/
|
||||
|
||||
import { vi } from 'vitest';
|
||||
import { readFile } from 'fs/promises';
|
||||
import esmock from 'esmock';
|
||||
import * as mockStore from './mocks/mockStore.js';
|
||||
import { send } from './mocks/mockNotification.js';
|
||||
|
||||
export const providerConfig = JSON.parse(await readFile(new URL('./provider/testProvider.json', import.meta.url)));
|
||||
|
||||
export const mockFredy = async () => {
|
||||
return await esmock('../lib/FredyPipelineExecutioner', {
|
||||
'../lib/services/storage/listingsStorage.js': {
|
||||
...mockStore,
|
||||
},
|
||||
'../lib/services/storage/settingsStorage.js': {
|
||||
...mockStore,
|
||||
},
|
||||
'../lib/services/geocoding/geoCodingService.js': {
|
||||
vi.mock('../lib/services/storage/listingsStorage.js', () => mockStore);
|
||||
vi.mock('../lib/services/storage/settingsStorage.js', () => mockStore);
|
||||
vi.mock('../lib/services/geocoding/geoCodingService.js', () => ({
|
||||
geocodeAddress: mockStore.getGeocoordinatesByAddress,
|
||||
},
|
||||
'../lib/services/storage/jobStorage.js': {
|
||||
}));
|
||||
vi.mock('../lib/services/storage/jobStorage.js', () => ({
|
||||
getJob: (jobKey) => ({ id: jobKey, userId: 'user1' }),
|
||||
},
|
||||
'../lib/notification/notify.js': {
|
||||
send,
|
||||
},
|
||||
});
|
||||
}));
|
||||
vi.mock('../lib/notification/notify.js', () => ({ send }));
|
||||
|
||||
export const mockFredy = async () => {
|
||||
const mod = await import('../lib/FredyPipelineExecutioner.js');
|
||||
return mod.default ?? mod;
|
||||
};
|
||||
|
||||
@@ -3,19 +3,19 @@
|
||||
* Licensed under Apache-2.0 with Commons Clause and Attribution/Naming Clause
|
||||
*/
|
||||
|
||||
import { expect } from 'chai';
|
||||
import { expect } from 'vitest';
|
||||
import { buildHash } from '../../lib/utils.js';
|
||||
|
||||
describe('utilsCheck', () => {
|
||||
describe('#utilsCheck()', () => {
|
||||
it('should be null when null input', () => {
|
||||
expect(buildHash(null)).to.be.null;
|
||||
expect(buildHash(null)).toBeNull();
|
||||
});
|
||||
it('should be null when null empty', () => {
|
||||
expect(buildHash('')).to.be.null;
|
||||
expect(buildHash('')).toBeNull();
|
||||
});
|
||||
it('should return a value', () => {
|
||||
expect(buildHash('bla', '', null)).to.be.a.string;
|
||||
expect(buildHash('bla', '', null)).toBeTypeOf('string');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
16
vitest.config.js
Normal file
16
vitest.config.js
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (c) 2026 by Christian Kellner.
|
||||
* Licensed under Apache-2.0 with Commons Clause and Attribution/Naming Clause
|
||||
*/
|
||||
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
globals: true,
|
||||
environment: 'node',
|
||||
include: ['test/**/*.test.js'],
|
||||
testTimeout: 60000,
|
||||
reporters: ['verbose'],
|
||||
},
|
||||
});
|
||||
21
vitest.gh.config.js
Normal file
21
vitest.gh.config.js
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) 2026 by Christian Kellner.
|
||||
* Licensed under Apache-2.0 with Commons Clause and Attribution/Naming Clause
|
||||
*/
|
||||
|
||||
import { defineConfig, mergeConfig } from 'vitest/config';
|
||||
import base from './vitest.config.js';
|
||||
|
||||
export default mergeConfig(
|
||||
base,
|
||||
defineConfig({
|
||||
test: {
|
||||
exclude: [
|
||||
'**/node_modules/**',
|
||||
'test/provider/immonet.test.js',
|
||||
'test/provider/immobilienDe.test.js',
|
||||
'test/provider/immowelt.test.js',
|
||||
],
|
||||
},
|
||||
}),
|
||||
);
|
||||
Reference in New Issue
Block a user