- App.tsx: full navigation (Auth stack + Main tabs with 5 screens) - Auth: LoginScreen, RegisterScreen, ForgotPasswordScreen - HomeScreen: dashboard with IoT metrics, weather widget, alerts, quick actions, sensors - MapScreen: interactive map with layer toggles (6 layers) - MarketplaceScreen: categories (6), products (5), search - ChatScreen: AI chat with quick prompts (4), bot responses - ProfileScreen: user info, stats, menu (9 items), logout - AlertsScreen: alert list with severity, acknowledge - SensorsScreen: sensor list with type filters (6 types), search - ZonesScreen: zone cards with stats - SettingsScreen: language picker (FR/EN/ES/DE), privacy, about - Stores: iotStore (sensors, zones, alerts), notificationStore, uiStore + i18n - Hooks: useSensors, useAlerts, useNotifications, useLocation - Components: Card, Button, LoadingSpinner, ErrorBoundary, Header - Services: iotService, notificationService (with axios API client) - Utils: formatters (temp, AQI, noise, dates), validators (email, password, IBAN) - Theme: colors.ts with full design system (Blue Ocean palette) - Ditto: fixed MongoDB connection, new JWT secrets, official gateway image
282 lines
5.2 KiB
JavaScript
282 lines
5.2 KiB
JavaScript
var test = require('ava')
|
|
var AssertionError = require('assert').AssertionError
|
|
var validate = require('./')
|
|
|
|
test('requires "anonymousId" to be a string or number', t => {
|
|
const event = {
|
|
type: 'track',
|
|
anonymousId: { foo: 'bar' }
|
|
}
|
|
t.throws(() => {
|
|
validate(event)
|
|
}, '"anonymousId" must be a string or number.')
|
|
})
|
|
|
|
test('requires "category" to be a string', t => {
|
|
const event = {
|
|
type: 'track',
|
|
category: true
|
|
}
|
|
t.throws(() => {
|
|
validate(event)
|
|
}, '"category" must be a string.')
|
|
})
|
|
|
|
test('requires "integrations" to be an object', t => {
|
|
const event = {
|
|
type: 'track',
|
|
integrations: true
|
|
}
|
|
t.throws(() => {
|
|
validate(event)
|
|
}, '"integrations" must be an object.')
|
|
})
|
|
|
|
test('requires an event type', t => {
|
|
t.throws(() => {
|
|
validate({})
|
|
}, AssertionError)
|
|
|
|
t.throws(() => {
|
|
validate({ type: '' }, null)
|
|
}, AssertionError)
|
|
})
|
|
|
|
test('requires a valid event type', t => {
|
|
t.throws(() => {
|
|
validate({ type: 'banana' })
|
|
}, 'Invalid event type: "banana"')
|
|
})
|
|
|
|
test('requires anonymousId or userId on track events', t => {
|
|
t.throws(() => {
|
|
validate({
|
|
type: 'track',
|
|
event: 'Did Something'
|
|
})
|
|
}, 'You must pass either an "anonymousId" or a "userId".')
|
|
|
|
t.throws(() => {
|
|
validate({
|
|
type: 'track',
|
|
event: 'Did Something',
|
|
fooId: 'banana'
|
|
})
|
|
}, 'You must pass either an "anonymousId" or a "userId".')
|
|
|
|
t.notThrows(() => {
|
|
validate({
|
|
event: 'Did Something',
|
|
anonymousId: 'banana'
|
|
}, 'track')
|
|
})
|
|
|
|
t.notThrows(() => {
|
|
validate({
|
|
type: 'track',
|
|
event: 'Did Something',
|
|
userId: 'banana'
|
|
})
|
|
})
|
|
})
|
|
|
|
test('requires event on track events', t => {
|
|
t.throws(() => {
|
|
validate({
|
|
type: 'track',
|
|
userId: 'banana'
|
|
})
|
|
}, 'You must pass an "event".')
|
|
|
|
t.notThrows(() => {
|
|
validate({
|
|
type: 'track',
|
|
event: 'Did Something',
|
|
userId: 'banana'
|
|
})
|
|
})
|
|
})
|
|
|
|
test('requires anonymousId or userId on group events', t => {
|
|
t.throws(() => {
|
|
validate({
|
|
type: 'group',
|
|
groupId: 'foo'
|
|
})
|
|
}, 'You must pass either an "anonymousId" or a "userId".')
|
|
|
|
t.throws(() => {
|
|
validate({
|
|
type: 'group',
|
|
groupId: 'foo',
|
|
fooId: 'banana'
|
|
})
|
|
}, 'You must pass either an "anonymousId" or a "userId".')
|
|
|
|
t.notThrows(() => {
|
|
validate({
|
|
type: 'group',
|
|
groupId: 'foo',
|
|
anonymousId: 'banana'
|
|
})
|
|
})
|
|
|
|
t.notThrows(() => {
|
|
validate({
|
|
type: 'group',
|
|
groupId: 'foo',
|
|
userId: 'banana'
|
|
})
|
|
})
|
|
})
|
|
|
|
test('requires groupId on group events', t => {
|
|
t.throws(() => {
|
|
validate({
|
|
type: 'group',
|
|
userId: 'banana'
|
|
})
|
|
}, 'You must pass a "groupId".')
|
|
|
|
t.notThrows(() => {
|
|
validate({
|
|
type: 'group',
|
|
groupId: 'foo',
|
|
userId: 'banana'
|
|
})
|
|
})
|
|
})
|
|
|
|
test('requires anonymousId or userId on identify events', t => {
|
|
t.throws(() => {
|
|
validate({
|
|
type: 'identify'
|
|
})
|
|
}, 'You must pass either an "anonymousId" or a "userId".')
|
|
|
|
t.throws(() => {
|
|
validate({
|
|
type: 'identify',
|
|
fooId: 'banana'
|
|
})
|
|
}, 'You must pass either an "anonymousId" or a "userId".')
|
|
|
|
t.notThrows(() => {
|
|
validate({
|
|
type: 'identify',
|
|
anonymousId: 'banana'
|
|
})
|
|
})
|
|
|
|
t.notThrows(() => {
|
|
validate({
|
|
type: 'identify',
|
|
userId: 'banana'
|
|
})
|
|
})
|
|
})
|
|
|
|
test('requires anonymousId or userId on page events', t => {
|
|
t.throws(() => {
|
|
validate({
|
|
type: 'page'
|
|
})
|
|
}, 'You must pass either an "anonymousId" or a "userId".')
|
|
|
|
t.throws(() => {
|
|
validate({
|
|
type: 'page',
|
|
fooId: 'banana'
|
|
})
|
|
}, 'You must pass either an "anonymousId" or a "userId".')
|
|
|
|
t.notThrows(() => {
|
|
validate({
|
|
type: 'page',
|
|
anonymousId: 'banana'
|
|
})
|
|
})
|
|
|
|
t.notThrows(() => {
|
|
validate({
|
|
type: 'page',
|
|
userId: 'banana'
|
|
})
|
|
})
|
|
})
|
|
|
|
test('requires anonymousId or userId on screen events', t => {
|
|
t.throws(() => {
|
|
validate({
|
|
type: 'screen'
|
|
})
|
|
}, 'You must pass either an "anonymousId" or a "userId".')
|
|
|
|
t.throws(() => {
|
|
validate({
|
|
type: 'screen',
|
|
fooId: 'banana'
|
|
})
|
|
}, 'You must pass either an "anonymousId" or a "userId".')
|
|
|
|
t.notThrows(() => {
|
|
validate({
|
|
type: 'screen',
|
|
anonymousId: 'banana'
|
|
})
|
|
})
|
|
|
|
t.notThrows(() => {
|
|
validate({
|
|
type: 'screen',
|
|
userId: 'banana'
|
|
})
|
|
})
|
|
})
|
|
|
|
test('requires userId on alias events', t => {
|
|
t.throws(() => {
|
|
validate({
|
|
type: 'alias'
|
|
})
|
|
}, 'You must pass a "userId".')
|
|
|
|
t.throws(() => {
|
|
validate({
|
|
type: 'alias',
|
|
fooId: 'banana'
|
|
})
|
|
}, 'You must pass a "userId".')
|
|
|
|
t.notThrows(() => {
|
|
validate({
|
|
type: 'alias',
|
|
userId: 'banana',
|
|
previousId: 'apple'
|
|
})
|
|
})
|
|
})
|
|
|
|
test('requires events to be < 32kb', t => {
|
|
t.throws(() => {
|
|
var event = {
|
|
type: 'track',
|
|
event: 'Did Something',
|
|
userId: 'banana',
|
|
properties: {}
|
|
}
|
|
for (var i = 0; i < 10000; i++) {
|
|
event.properties[i] = 'a'
|
|
}
|
|
validate(event)
|
|
}, 'Your message must be < 32kb.')
|
|
|
|
t.notThrows(() => {
|
|
validate({
|
|
type: 'track',
|
|
event: 'Did Something',
|
|
userId: 'banana'
|
|
})
|
|
})
|
|
})
|