Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions docs/cli/sandbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,14 @@ b2c sandbox list --json
### Output

```
Realm Instance State Profile Created EOL
Realm Instance State Profile Created EOL
──────────────────────────────────────────────────────────────────────────
abcd 001 started medium 12/20/2024, 10:00 AM 12/21/2024, 10:00 AM
abcd 002 stopped large 12/19/2024, 2:30 PM 12/20/2024, 2:30 PM
abcd 001 started medium 2024-12-20 2024-12-21
abcd 002 stopped large 2024-12-19 2024-12-20 22:30
```

The `EOL` column displays `YYYY-MM-DD` normally. When a sandbox expires within 24 hours (or is already expired), the time is also shown as `YYYY-MM-DD HH:mm` (UTC).

---

## b2c sandbox create
Expand Down
15 changes: 13 additions & 2 deletions packages/b2c-cli/src/commands/sandbox/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ interface OdsListResponse {
data: SandboxModel[];
}

const COLUMNS: Record<string, ColumnDef<SandboxModel>> = {
export const COLUMNS: Record<string, ColumnDef<SandboxModel>> = {
realm: {
header: 'Realm',
get: (s) => s.realm || '-',
Expand All @@ -41,7 +41,18 @@ const COLUMNS: Record<string, ColumnDef<SandboxModel>> = {
},
eol: {
header: 'EOL',
get: (s) => (s.eol ? new Date(s.eol).toISOString().slice(0, 10) : '-'),
get(s) {
if (!s.eol) return '-';
const d = new Date(s.eol);
const date = d.toISOString().slice(0, 10);
const msUntilEol = d.getTime() - Date.now();
if (msUntilEol <= 24 * 60 * 60 * 1000) {
const hh = String(d.getUTCHours()).padStart(2, '0');
const mm = String(d.getUTCMinutes()).padStart(2, '0');
return `${date} ${hh}:${mm}`;
}
return date;
},
},
id: {
header: 'ID',
Expand Down
40 changes: 39 additions & 1 deletion packages/b2c-cli/test/commands/sandbox/list.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import {expect} from 'chai';
import sinon from 'sinon';
import SandboxList from '../../../src/commands/sandbox/list.js';
import SandboxList, {COLUMNS} from '../../../src/commands/sandbox/list.js';
import {isolateConfig, restoreConfig} from '@salesforce/b2c-tooling-sdk/test-utils';
import {runSilent} from '../../helpers/test-setup.js';

Expand Down Expand Up @@ -97,6 +97,44 @@ describe('sandbox list', () => {
});
});

describe('eol column formatting', () => {
const getEol = COLUMNS.eol.get;

it('returns "-" when eol is missing', () => {
expect(getEol({} as any)).to.equal('-');
});

it('returns YYYY-MM-DD when EOL is more than 24 hours away', () => {
const future = new Date(Date.now() + 48 * 60 * 60 * 1000).toISOString();
const result = getEol({eol: future} as any);
expect(result).to.match(/^\d{4}-\d{2}-\d{2}$/);
});

it('returns YYYY-MM-DD HH:mm when EOL is within 24 hours', () => {
const soon = new Date(Date.now() + 2 * 60 * 60 * 1000).toISOString();
const result = getEol({eol: soon} as any);
expect(result).to.match(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/);
});

it('returns YYYY-MM-DD HH:mm for an already-expired EOL', () => {
const past = new Date(Date.now() - 60 * 60 * 1000).toISOString();
const result = getEol({eol: past} as any);
expect(result).to.match(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/);
});

it('returns correct UTC time in YYYY-MM-DD HH:mm format', () => {
// Fixed timestamp: 2026-02-20T09:30:00Z — within 24h from now in the test
const eolTime = new Date(Date.now() + 30 * 60 * 1000).toISOString(); // 30 minutes away
const d = new Date(eolTime);
const expectedDate = d.toISOString().slice(0, 10);
const expectedHH = String(d.getUTCHours()).padStart(2, '0');
const expectedMM = String(d.getUTCMinutes()).padStart(2, '0');

const result = getEol({eol: eolTime} as any);
expect(result).to.equal(`${expectedDate} ${expectedHH}:${expectedMM}`);
});
});

describe('filter parameter building', () => {
it('should build filter params from realm flag', () => {
const command = new SandboxList([], {} as any);
Expand Down
Loading