chore: initial commit
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import {EventEmitter} from 'node:events';
|
||||
import {createRequire} from 'node:module';
|
||||
import {test} from 'node:test';
|
||||
import {tsImport} from 'tsx/esm/api';
|
||||
|
||||
const {init: InventoryInfo} = await tsImport('../bot/info/bot/inventory.ts', import.meta.url);
|
||||
const {create_info_manager} = await tsImport('../bot/info/init.ts', import.meta.url);
|
||||
const require = createRequire(import.meta.url);
|
||||
const mf = createRequire(require.resolve('@wp2508/mineflayer'));
|
||||
const registry = mf('prismarine-registry')('1.21.4');
|
||||
const windows = mf('prismarine-windows')(registry);
|
||||
const Item = mf('prismarine-item')(registry);
|
||||
|
||||
function createBot() {
|
||||
const bot = new EventEmitter();
|
||||
bot._client = new EventEmitter();
|
||||
bot.inventory = windows.createWindow(0, 'minecraft:inventory', 'Inventory');
|
||||
bot.currentWindow = null;
|
||||
bot.quickBarSlot = 2;
|
||||
bot.registry = registry;
|
||||
return bot;
|
||||
}
|
||||
|
||||
test('inventory snapshots cover all nine window kinds and special properties without operations', t => {
|
||||
t.mock.timers.enable({apis: ['setTimeout']});
|
||||
const bot = createBot();
|
||||
const info = new InventoryInfo(bot);
|
||||
t.after(() => info.dispose());
|
||||
const tick = () => t.mock.timers.tick(50);
|
||||
const open = (type, id = 1) => {
|
||||
const window = windows.createWindow(id, `minecraft:${type}`, type);
|
||||
bot.currentWindow = window;
|
||||
bot.emit('windowOpen', window);
|
||||
tick();
|
||||
return window;
|
||||
};
|
||||
const property = (id, value) => bot._client.emit('craft_progress_bar', {
|
||||
windowId: bot.currentWindow.id, property: id, value,
|
||||
});
|
||||
|
||||
assert.equal(info.get_info().window.kind, 'inventory');
|
||||
assert.equal(info.get_info().handSlot, 38);
|
||||
assert.equal(bot.listenerCount('end'), 0);
|
||||
for (const [type, kind, handSlot] of [
|
||||
['generic_9x3', 'chest', 56],
|
||||
['generic_9x6', 'large_chest', 83],
|
||||
['crafting', 'crafting', 39],
|
||||
['enchantment', 'enchantment', 31],
|
||||
['stonecutter', 'stonecutter', 31],
|
||||
['furnace', 'furnace', 32],
|
||||
['merchant', 'merchant', 32],
|
||||
['lectern', 'lectern', null],
|
||||
]) {
|
||||
const previous = bot.currentWindow;
|
||||
const window = open(type);
|
||||
assert.equal(info.get_info().window.kind, kind);
|
||||
assert.equal(info.get_info().handSlot, handSlot);
|
||||
if (previous) assert.equal(previous.listenerCount('updateSlot'), 0);
|
||||
if (kind === 'lectern') {
|
||||
assert.equal(info.get_info().window.hasPlayerInventory, false);
|
||||
assert.equal(info.get_info().window.inventoryStart, null);
|
||||
assert.equal(info.get_info().window.slots.length, 1);
|
||||
}
|
||||
assert.equal(window.listenerCount('updateSlot'), 1);
|
||||
}
|
||||
|
||||
open('furnace');
|
||||
assert.equal(info.get_info().special.furnace.fuelRatio, null);
|
||||
property(0, 40);
|
||||
property(1, 80);
|
||||
property(2, 50);
|
||||
property(3, 200);
|
||||
tick();
|
||||
assert.equal(info.get_info().special.furnace.fuelRatio, 0.5);
|
||||
assert.equal(info.get_info().special.furnace.progressRatio, 0.25);
|
||||
open('furnace');
|
||||
assert.deepEqual(info.get_info().window.properties, {});
|
||||
|
||||
// Properties can arrive before windowOpen, while Mineflayer awaits slot contents.
|
||||
bot.currentWindow = windows.createWindow(2, 'minecraft:enchantment', 'Enchant');
|
||||
property(0, 30);
|
||||
property(4, 5);
|
||||
property(7, 3);
|
||||
bot.emit('windowOpen', bot.currentWindow);
|
||||
tick();
|
||||
const option = info.get_info().special.enchantment.options[0];
|
||||
assert.deepEqual(option, {index: 0, requiredLevel: 30, levelCost: 1, lapisCost: 1, enchantmentId: 5, enchantmentLevel: 3});
|
||||
assert.equal(info.get_info().special.enchantment.options[1].requiredLevel, null);
|
||||
|
||||
const merchant = open('merchant');
|
||||
assert.equal(info.get_info().special.merchant.ready, false);
|
||||
merchant.trades = [{
|
||||
inputItem1: new Item(registry.itemsByName.emerald.id, 2),
|
||||
outputItem: new Item(registry.itemsByName.bread.id, 1),
|
||||
hasItem2: false, realPrice: 1, tradeDisabled: false, nbTradeUses: 0, maximumNbTradeUses: 12,
|
||||
}];
|
||||
bot._client.emit('trade_list');
|
||||
tick();
|
||||
const offers = info.get_info().special.merchant.offers;
|
||||
assert.equal(offers[0].input1.type, registry.itemsByName.emerald.id);
|
||||
assert.equal(offers[0].realPrice, 1);
|
||||
assert.equal(offers[0].input2, null);
|
||||
|
||||
const lectern = open('lectern');
|
||||
const book = new Item(registry.itemsByName.written_book.id, 1);
|
||||
book.components = [{type: 'written_book_content', data: {
|
||||
rawTitle: 'Title', author: 'Author', pages: [{content: {
|
||||
type: 'compound', value: {text: {type: 'string', value: 'Page'}, color: {type: 'string', value: 'red'}},
|
||||
}}],
|
||||
}}];
|
||||
lectern.updateSlot(0, book);
|
||||
property(0, 0);
|
||||
tick();
|
||||
assert.equal(info.get_info().special.lectern.book.title, 'Title');
|
||||
assert.equal(info.get_info().special.lectern.book.pages[0][0].message, 'Page');
|
||||
assert.equal(info.get_info().special.lectern.book.pages[0][0].font_color, '#FF5555');
|
||||
assert.equal(info.get_info().special.lectern.page, 0);
|
||||
const oldBook = {
|
||||
type: book.type, name: 'written_book', displayName: 'Written Book', count: 1, metadata: 0,
|
||||
nbt: {type: 'compound', value: {
|
||||
title: {type: 'string', value: 'Legacy'}, author: {type: 'string', value: 'Writer'},
|
||||
pages: {type: 'list', value: {type: 'string', value: ['{"text":"Old page"}']}},
|
||||
long: {type: 'long', value: 123n}, bytes: Buffer.from([1, 2]),
|
||||
}},
|
||||
};
|
||||
lectern.slots[0] = oldBook;
|
||||
lectern.emit('updateSlot');
|
||||
tick();
|
||||
assert.equal(info.get_info().special.lectern.book.pages[0][0].message, 'Old page');
|
||||
assert.equal(info.get_info().window.slots[0].details.extra.legacy.long, '123');
|
||||
assert.deepEqual(info.get_info().window.slots[0].details.extra.legacy.bytes, [1, 2]);
|
||||
assert.equal('nbt' in info.get_info().window.slots[0], false);
|
||||
assert.doesNotThrow(() => JSON.stringify(info.get_info()));
|
||||
assert.equal('icon' in info.get_info().window.slots[0], false);
|
||||
|
||||
const cyclic = {number: 1n};
|
||||
cyclic.self = cyclic;
|
||||
Object.defineProperty(cyclic, 'accessor', {enumerable: true, get() {throw new Error('must not invoke');}});
|
||||
oldBook.components = [{type: 'custom_data', data: cyclic}];
|
||||
lectern.emit('updateSlot');
|
||||
tick();
|
||||
const data = info.get_info().window.slots[0].details.extra.custom_data;
|
||||
assert.deepEqual(data, {number: '1', self: null, accessor: null});
|
||||
assert.equal(info.get_info().window.slots[0].truncated, true);
|
||||
|
||||
bot.currentWindow = null;
|
||||
bot.emit('windowClose', lectern);
|
||||
tick();
|
||||
assert.equal(info.get_info().window.kind, 'inventory');
|
||||
assert.deepEqual(info.get_info().special, {});
|
||||
assert.equal(lectern.listenerCount('updateSlot'), 0);
|
||||
let updates = 0;
|
||||
const unsubscribe = info.on_update(() => updates++);
|
||||
bot.inventory.selectedItem = new Item(registry.itemsByName.stone.id, 3);
|
||||
bot._client.emit('set_slot');
|
||||
tick();
|
||||
assert.equal(info.get_info().cursorItem.count, 3);
|
||||
assert.equal(info.get_info().cursorItem.slot, null);
|
||||
bot.emit('physicsTick');
|
||||
tick();
|
||||
assert.equal(updates, 1);
|
||||
unsubscribe();
|
||||
bot.emit('physicsTick');
|
||||
info.dispose();
|
||||
tick();
|
||||
assert.equal(bot.eventNames().length, 0);
|
||||
assert.equal(bot._client.eventNames().length, 0);
|
||||
assert.equal(bot.inventory.listenerCount('updateSlot'), 0);
|
||||
assert.throws(() => info.on_update(() => {}), /已销毁/);
|
||||
});
|
||||
|
||||
test('the real info manager loads inventory and owns its cleanup', async () => {
|
||||
const bot = createBot();
|
||||
const manager = await create_info_manager(bot);
|
||||
assert.equal(bot.listenerCount('end'), 1);
|
||||
assert.equal(manager.get_info('inventory').value.window.kind, 'inventory');
|
||||
assert.ok(manager.get_info('inventory').i18n.fields.window.fields.slots.fields.type);
|
||||
bot.emit('end');
|
||||
manager.dispose();
|
||||
assert.equal(bot.eventNames().length, 0);
|
||||
assert.equal(bot._client.eventNames().length, 0);
|
||||
assert.equal(bot.inventory.listenerCount('updateSlot'), 0);
|
||||
});
|
||||
Reference in New Issue
Block a user