A zone is an isolated portion of an @xstate/test model. A zone defines the events it uses, the event that causes it to be entered and the states within the zone
The zones entryEvent is added as an event handler to the zones parent initial state. If this is a root zone passed to buildTestModel
this is the root state, if it's a sub zone (a state pointing at a zone), that is the initial state of the zone that state is contained in
Each state in a zone must contain a test property that validates that the state has been entered. At least one of the states requires it to be marked as the initial state of the zone with initial: true
An event handler in a zone state can only refer to events defined inside of the zone
A zone looks like this
{
events: {
EVENT_NAME: (model) => ... // some method call on the model that returns a Promise
SOME_OTHER_EVENT: (model) => ...,
},
entryEvent: "EVENT_NAME", // the name of one of the events in the event object, this event must cause Rimu to transition to the initial state of the zone
states: {
stateName: {
initial: true, // This indicates the initial state of the zone, what running the `entryEvent` handler (EVENT_NAME defined above) in the initial state of the parent zone would cause Rimu to transition to
test: (model) => ... // Returns a Promise that resolves if the state has been entered correctly and rejects otherwise
on: {
SOME_OTHER_EVENT: "someOtherState" // indicate that having the test model run SOME_OTHER_EVENT in this state will cause Rimu to enter the someOtherState, validated by that states test function
}
},
someOtherState: {
test: (model) => ...
}
}
}
A zone can have a state that points at another zone, allowing separation of tests into multiple zones. This is as simple as defining a state like stateName: zone
When a state refers to a zone like this the entry event to the zone is added to the initial state of the containing zone, transitioning to the initial state of the sub zone
Zones can be nested infinitely
Assuming we have a configuration of zones like so
const zone1 = buildTestZone({
events: {
ZONE1_ENTRY: () => ...,
ZONE1_TRANSITION: () => ...,
},
entryEvent: "ZONE1_ENTRY",
states: {
zone1Initial: {
test: () => ...,
initial: true,
on: {
ZONE1_TRANSITION: "zone1OtherState"
}
},
zone1OtherState: {
test: () => ...,
}
}
});
const subZone1 = buildTestZone({
events: {
SUB_ZONE1_ENTRY: () => ...,
SUB_ZONE1_TRANSITION: () => ...,
},
entryEvent: "SUB_ZONE1_ENTRY",
states: {
subZone1Initial: {
test: () => ...,
initial: true,
on: {
SUB_ZONE1_TRANSITION: "subZone1OtherState"
}
},
subZone1OtherState: {
test: () => ...,
}
}
});
const zone2 = buildTestZone({
events: {
ZONE2_ENTRY: () => ...,
},
entryEvent: "ZONE2_ENTRY",
states: {
zone2Initial: {
test: () => ...,
initial: true,
},
zone2SubZone: subZone1
}
});
const model = buildTestModel({
rootState: {
test: () => ...
}
}, [zone1, zone2]);
then the following test model will be created
{
initial: "rootState",
states: {
rootState: {
on: {
ZONE1_ENTRY: "zone1Initial",
ZONE2_ENTRY: "zone2Initial"
}
},
zone1Initial: {
on: {
ZONE1_TRANSITION: "zone1OtherState",
}
},
zone1OtherState: {},
zone2Initial: {
initial: "zone2Initial",
states: {
zone2Initial: {
on: {
SUB_ZONE1_ENTRY: "subZone1Initial",
}
},
subZone1Initial: {
on: {
SUB_ZONE1_TRANSITION: "subZone1OtherState"
}
},
subZone1OtherState: {}
}
}
}
}
with all of the events extracted from each zone (and recursively from sub zones) and supplied to the models withEvents
function automatically