@cosmospro/cosmospro-msw
v1.0.5
Published
Biblioteca com a implementação odata para utilização dentro do MSW, permitindo mock de apis odata para teste.
Maintainers
Readme
@cosmospro/cosmospro-msw
Wrapper declarativo de MSW para mockar Custom Views e Custom Actions do Cosmos Pro em testes e desenvolvimento — sem precisar conhecer MSW ou OData diretamente.
A biblioteca encapsula o protocolo OData v4 (filtros, paginação, ordenação, agregação) e o endpoint ExecuteCustomAction em duas funções declarativas: você informa o nome e os dados/handler, e a lib cuida do resto.
Internamente apoia-se em @cosmospro/msw-odata para o parser OData e os helpers de autenticação.
Instalação
npm i -D @cosmospro/cosmospro-msw mswmsw é uma peer dependency — instale a versão 2.x.
Quickstart (Vitest + Node)
import { setupServer } from 'msw/node';
import { mockCustomView, mockCustomAction } from '@cosmospro/cosmospro-msw';
interface Produto { id: number; nome: string; preco: number }
const produtos: Produto[] = [
{ id: 1, nome: 'Notebook', preco: 5000 },
{ id: 2, nome: 'Mouse', preco: 80 },
];
const server = setupServer(
mockCustomView<Produto>('ProdutosMaisVendidos', produtos),
mockCustomAction('EnviarEmail', ({ body }) => ({ status: 'sent' })),
);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());No browser, troque por setupWorker de msw/browser.
mockCustomView<T>(name, data, options?)
Intercepta as Custom Views do Cosmos Pro nas duas formas suportadas pelo backend real:
| Método | URL | Fonte dos Parameters |
|--------|-------------------------------------------------------------------------|-----------------------------------------|
| POST | {baseUrl}/odata/CustomViews(Name='{name}')/ExecuteAndReceiveData | body.Parameters (JSON) |
| GET | {baseUrl}/rest/api/CustomViews(Name='{name}')/ExecuteAndReceiveData | chaves da query string sem prefixo $ |
Os system query options OData ($filter, $top, $skip, $orderby, $select, $count, $apply, $expand) são lidos igualmente da query string nos dois modos. Por padrão a lib registra os dois simultaneamente — use methods se quiser restringir.
mockCustomView<Produto>('ProdutosMaisVendidos', produtos, {
baseUrl: 'http://api.example.com', // opcional - default: qualquer host
auth: 'meu-bearer-token', // string => Bearer | AuthPredicate => custom
delay: 200, // ms de latencia simulada
onParams: (params) => {
// POST -> recebe body.Parameters
// GET -> recebe as chaves nao-$ da query string (valores sao strings)
return produtos.filter((p) => p.preco >= Number(params.MinPrice));
},
// Habilitar operadores $filter que a sua Custom View suporta (ver secao abaixo)
filterCapabilities: { allow: ['eq', 'ne', 'gt', 'lt', 'and', 'or', 'contains'] },
// Opcional — restringe os metodos/paths interceptados (default: ambos).
methods: ['GET', 'POST'],
});Restringir métodos com methods
Por padrão a lib intercepta GET e POST simultaneamente. Para mockar apenas uma forma:
// So GET (path /rest/api/CustomViews(...))
mockCustomView('Produtos', produtos, { methods: 'GET' });
// So POST (path /odata/CustomViews(...))
mockCustomView('Produtos', produtos, { methods: 'POST' });Requisições na combinação "errada" (ex.: POST em /rest/api/... ou GET em /odata/...) são repassadas adiante para outros handlers do worker — não são interceptadas.
OData suportado (delegado ao @cosmospro/msw-odata):
$filter, $orderby, $top, $skip, $select, $count, $expand, $apply.
Resposta:
{
"@odata.count": 42,
"value": [ /* itens */ ]
}Filtros $filter — modelo opt-in
Por padrao, todos os operadores e funcoes de $filter estao desabilitados. Qualquer query com $filter retorna 400 Bad Request automaticamente — espelhando o comportamento do Cosmos Pro real, que nao suporta 100% da spec OData.
Habilite explicitamente os operadores que a sua Custom View aceita usando filterCapabilities:
import { mockCustomView } from '@cosmospro/cosmospro-msw';
import type { FilterCapabilities } from '@cosmospro/cosmospro-msw';
// Apenas igualdade, comparacoes basicas e conjuncao/disjuncao
mockCustomView('Produtos', produtos, {
filterCapabilities: { allow: ['eq', 'ne', 'gt', 'ge', 'lt', 'le', 'and', 'or'] },
});
// Inclui funcoes de string tambem
mockCustomView('Clientes', clientes, {
filterCapabilities: {
allow: ['eq', 'ne', 'and', 'or', 'contains', 'startswith', 'endswith'],
},
});
// Permitir tudo (equivale ao comportamento antigo da lib)
mockCustomView('Qualquer', dados, {
filterCapabilities: { deny: [] },
});Operadores disponíveis para allowlist/denylist:
| Categoria | Tokens |
| --- | --- |
| Comparacao | eq ne gt ge lt le in has |
| Logica | and or not |
| Aritmetica | add sub mul div mod |
| String | contains startswith endswith length indexof substring tolower toupper trim concat |
| Data | year month day hour minute second now date time |
| Matematica | round floor ceiling |
Quando um operador nao permitido e usado, o mock responde:
{ "error": "Filter operator/function 'contains' is not supported by the configured capabilities" }com status HTTP 400.
mockCustomAction(name, handler, options?)
Intercepta POST {baseUrl}/api/ExecuteCustomAction/ExecuteAction?ActionName={name}&api-version=1.0. O handler recebe um contexto e devolve qualquer JSON-serializavel (ou um Response via ctx.reply.*).
mockCustomAction('AtualizarStatus', async ({ query, body, reply }) => {
if (!body || typeof body !== 'object') {
return reply.badRequest('body obrigatorio');
}
return { id: query.id, status: (body as any).novoStatus };
});Contexto disponivel:
| Campo | Tipo | Descricao |
| --- | --- | --- |
| request | Request | Requisicao original (Fetch API) |
| query | Record<string,string> | Query params (sem ActionName nem api-version) |
| body | unknown | JSON parseado do body, ou null se vazio/nao-JSON |
| reply | ReplyHelpers | Atalhos: json, ok, created, noContent, badRequest, unauthorized, forbidden, notFound |
O retorno do handler e envelopado automaticamente em HttpResponse.json(...). Se voce quer controlar status/headers, retorne um Response diretamente (use os helpers de ctx.reply).
Multiplas chamadas para mockCustomAction no mesmo setupServer convivem sem conflito: cada uma so responde quando o ActionName da query bate com o registrado.
Autenticacao
A opcao auth aceita:
- String - tratada como Bearer token (match exato em
Authorization: Bearer <token>). - Funcao
AuthPredicate-(request: Request) => boolean | Promise<boolean>.
Quando o predicado retorna false, a resposta e 401 Unauthorized.
import { authBearer } from '@cosmospro/cosmospro-msw';
mockCustomView('X', data, { auth: 'token' }); // bearer simples
mockCustomView('X', data, { auth: authBearer((t) => t.startsWith('sk_')) }); // validacao custom
mockCustomView('X', data, { auth: (req) => req.headers.has('x-key') }); // predicate customLimitacoes conhecidas
- Custom Views sao tratadas exclusivamente como OData v4 (formato real do Cosmos Pro).
- Em modo GET, os
Parameterschegam aoonParamscomo strings (assim comoURLSearchParamsentrega). Faça coerção (Number,new Date, etc.) dentro do callback — mesmo padrão que o POST já usava. - Chaves customizadas com prefixo
$em modo GET seriam silenciosamente tratadas como system query options. Evite (o backend real do Cosmos Pro também reserva o$). - Custom Actions com upload multipart de arquivos ainda nao sao cobertas (apenas body JSON).
- A regex de match assume URL absoluta na request -
setupWorker/setupServerinterceptam normalmente; em ambientes que reescrevem hosts, configurebaseUrlexplicitamente.
