Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
173 lines
6.1 KiB
TypeScript
173 lines
6.1 KiB
TypeScript
import { existsSync, readFileSync } from 'node:fs';
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { describe, expect, it } from 'vitest';
|
|
|
|
const testDir = path.dirname(fileURLToPath(import.meta.url));
|
|
const packageRoot = path.resolve(testDir, '..');
|
|
|
|
function read(filePath: string): string {
|
|
return readFileSync(path.join(packageRoot, filePath), 'utf8');
|
|
}
|
|
|
|
describe('Documentation coverage', () => {
|
|
it('T043: README includes credential setup, operation matrix, and ticket field requirements', () => {
|
|
const readme = read('README.md');
|
|
|
|
expect(readme).toContain('## Credential Setup');
|
|
expect(readme).toContain('## Operation Matrix');
|
|
expect(readme).toContain('## Ticket Field Requirements');
|
|
expect(readme).toContain('## Contact Field Requirements');
|
|
expect(readme).toContain('baseUrl');
|
|
expect(readme).toContain('apiKey');
|
|
});
|
|
|
|
it('T044: README installation covers self-hosted npm/manual paths and cloud limitation', () => {
|
|
const readme = read('README.md');
|
|
|
|
expect(readme).toContain('Self-hosted n8n');
|
|
expect(readme).toContain('npm install n8n-nodes-alga-psa');
|
|
expect(readme).toContain('unverified');
|
|
expect(readme).toContain('n8n Cloud');
|
|
});
|
|
|
|
it('T045: example workflows include create->update-assignment and search->update-status', () => {
|
|
const createPath = path.join(packageRoot, 'examples/create-update-assignment.workflow.json');
|
|
const searchPath = path.join(packageRoot, 'examples/search-update-status.workflow.json');
|
|
|
|
expect(existsSync(createPath)).toBe(true);
|
|
expect(existsSync(searchPath)).toBe(true);
|
|
|
|
const createWorkflow = JSON.parse(readFileSync(createPath, 'utf8')) as {
|
|
nodes: Array<{ parameters?: Record<string, unknown> }>;
|
|
};
|
|
const searchWorkflow = JSON.parse(readFileSync(searchPath, 'utf8')) as {
|
|
nodes: Array<{ parameters?: Record<string, unknown> }>;
|
|
};
|
|
|
|
expect(
|
|
createWorkflow.nodes.some(
|
|
(node) => node.parameters?.ticketOperation === 'create',
|
|
),
|
|
).toBe(true);
|
|
expect(
|
|
createWorkflow.nodes.some(
|
|
(node) => node.parameters?.ticketOperation === 'updateAssignment',
|
|
),
|
|
).toBe(true);
|
|
|
|
expect(
|
|
searchWorkflow.nodes.some(
|
|
(node) => node.parameters?.ticketOperation === 'search',
|
|
),
|
|
).toBe(true);
|
|
expect(
|
|
searchWorkflow.nodes.some(
|
|
(node) => node.parameters?.ticketOperation === 'updateStatus',
|
|
),
|
|
).toBe(true);
|
|
});
|
|
|
|
it('T053: README documents ticket comment operations without unsupported time_spent input', () => {
|
|
const readme = read('README.md');
|
|
const examplePath = path.join(packageRoot, 'examples/add-comment-then-list-comments.workflow.json');
|
|
|
|
expect(readme).toContain('## Ticket Comment Operations');
|
|
expect(readme).toContain('List Comments');
|
|
expect(readme).toContain('Add Comment');
|
|
expect(readme).toContain('time_spent');
|
|
expect(readme).toContain('not exposed');
|
|
expect(existsSync(examplePath)).toBe(true);
|
|
|
|
const commentWorkflow = JSON.parse(readFileSync(examplePath, 'utf8')) as {
|
|
nodes: Array<{ parameters?: Record<string, unknown> }>;
|
|
};
|
|
|
|
expect(
|
|
commentWorkflow.nodes.some(
|
|
(node) => node.parameters?.ticketOperation === 'addComment',
|
|
),
|
|
).toBe(true);
|
|
expect(
|
|
commentWorkflow.nodes.some(
|
|
(node) => node.parameters?.ticketOperation === 'listComments',
|
|
),
|
|
).toBe(true);
|
|
});
|
|
|
|
it('T034: README operation matrix includes contact CRUD operations', () => {
|
|
const readme = read('README.md');
|
|
|
|
expect(readme).toContain('| Contact | Create, Get, List, Update, Delete |');
|
|
});
|
|
|
|
it('T035: README describes contact field scope, lookup behavior, and list/delete output expectations', () => {
|
|
const readme = read('README.md');
|
|
|
|
expect(readme).toContain('full_name');
|
|
expect(readme).toContain('phone_numbers');
|
|
expect(readme).toContain('From List');
|
|
expect(readme).toContain('By ID');
|
|
expect(readme).toContain('Contact -> List');
|
|
expect(readme).toContain('Contact -> Delete');
|
|
});
|
|
|
|
it('T036: contact example workflow is present and referenced by the README', () => {
|
|
const readme = read('README.md');
|
|
const examplePath = path.join(packageRoot, 'examples/create-update-contact.workflow.json');
|
|
|
|
expect(readme).toContain('examples/create-update-contact.workflow.json');
|
|
expect(existsSync(examplePath)).toBe(true);
|
|
|
|
const workflow = JSON.parse(readFileSync(examplePath, 'utf8')) as {
|
|
nodes: Array<{ parameters?: Record<string, unknown> }>;
|
|
};
|
|
|
|
expect(
|
|
workflow.nodes.some(
|
|
(node) => node.parameters?.contactOperation === 'create',
|
|
),
|
|
).toBe(true);
|
|
expect(
|
|
workflow.nodes.some(
|
|
(node) => node.parameters?.contactOperation === 'update',
|
|
),
|
|
).toBe(true);
|
|
});
|
|
|
|
it('T037: release notes mention the contact CRUD expansion and its first-pass scope', () => {
|
|
const releaseNotes = read('RELEASE_NOTES.md');
|
|
|
|
expect(releaseNotes).toContain('Contact');
|
|
expect(releaseNotes).toContain('Create');
|
|
expect(releaseNotes).toContain('Get');
|
|
expect(releaseNotes).toContain('List');
|
|
expect(releaseNotes).toContain('Update');
|
|
expect(releaseNotes).toContain('Delete');
|
|
expect(releaseNotes).toContain('phone_numbers');
|
|
});
|
|
|
|
it('T042: contact docs and example workflow show the hybrid email fields', () => {
|
|
const readme = read('README.md');
|
|
const example = JSON.parse(read('examples/create-update-contact.workflow.json')) as {
|
|
nodes: Array<{ parameters?: Record<string, any> }>;
|
|
};
|
|
|
|
expect(readme).toContain('primary_email_canonical_type');
|
|
expect(readme).toContain('primary_email_custom_type');
|
|
expect(readme).toContain('additional_email_addresses');
|
|
|
|
const createNode = example.nodes.find(
|
|
(node) => node.parameters?.contactOperation === 'create',
|
|
);
|
|
expect(createNode?.parameters?.contactCreateAdditionalFields).toMatchObject({
|
|
primary_email_canonical_type: 'billing',
|
|
});
|
|
expect(
|
|
String(
|
|
createNode?.parameters?.contactCreateAdditionalFields?.additional_email_addresses ?? '',
|
|
),
|
|
).toContain('ada.personal@example.com');
|
|
});
|
|
});
|