phaser
Version:
A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers from the team at Phaser Studio Inc.
649 lines (497 loc) • 23.1 kB
JavaScript
var PCTAtlasFile = require('../../../src/loader/filetypes/PCTAtlasFile');
function createMockLoader ()
{
var loader = {
prefix: '',
path: '',
baseURL: '',
multiKeyIndex: 0,
imageLoadType: 'XHR',
textureManager: {
addAtlasPCT: vi.fn()
},
cacheManager: {
atlas: {
add: vi.fn()
}
},
addFile: vi.fn(),
setBaseURL: vi.fn(function (url) { loader.baseURL = url; }),
setPath: vi.fn(function (path) { loader.path = path; }),
setPrefix: vi.fn(function (prefix) { loader.prefix = prefix; })
};
return loader;
}
// A minimal decoded PCT structure with two pages, used by several addToCache tests.
function makeTwoPageDecoded ()
{
return {
pages: [
{ filename: 'atlas_0.png', format: 'RGBA8888', width: 512, height: 512, padding: 0 },
{ filename: 'atlas_1.png', format: 'RGBA8888', width: 256, height: 256, padding: 0 }
],
folders: [],
frames: {
a: { key: 'a', page: 0, x: 0, y: 0, w: 10, h: 10, trimmed: false, rotated: false, sourceW: 10, sourceH: 10, trimX: 0, trimY: 0 },
b: { key: 'b', page: 1, x: 0, y: 0, w: 20, h: 20, trimmed: false, rotated: false, sourceW: 20, sourceH: 20, trimX: 0, trimY: 0 }
}
};
}
describe('Phaser.Loader.FileTypes.PCTAtlasFile', function ()
{
// -------------------------------------------------------------------------
// Constructor (string key)
// -------------------------------------------------------------------------
describe('constructor (string key)', function ()
{
it('should set type to pctatlas', function ()
{
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, 'atlas1', 'atlas.pct');
expect(file.type).toBe('pctatlas');
});
it('should set key from the string key argument', function ()
{
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, 'myatlas', 'atlas.pct');
expect(file.key).toBe('myatlas');
});
it('should create exactly one child file (the PCT data file)', function ()
{
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, 'atlas1', 'atlas.pct');
expect(file.files.length).toBe(1);
});
it('should set the child file type to pct', function ()
{
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, 'atlas1', 'atlas.pct');
expect(file.files[0].type).toBe('pct');
});
it('should target the atlas cache for the child file', function ()
{
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, 'atlas1', 'atlas.pct');
expect(file.files[0].cache).toBe(loader.cacheManager.atlas);
});
it('should use the supplied URL for the child file', function ()
{
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, 'atlas1', 'custom/path.pct');
expect(file.files[0].url).toBe('custom/path.pct');
});
it('should default the extension to pct when no URL is given', function ()
{
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, 'atlas1');
// File base class auto-generates url from key + extension when url undefined
expect(file.files[0].url).toBe('atlas1.pct');
});
it('should set config.path when provided', function ()
{
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, 'atlas1', 'atlas.pct', 'textures/');
expect(file.config.path).toBe('textures/');
});
it('should set config.baseURL when provided', function ()
{
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, 'atlas1', 'atlas.pct', null, 'http://cdn.example.com/');
expect(file.config.baseURL).toBe('http://cdn.example.com/');
});
it('should set config.textureXhrSettings when provided', function ()
{
var loader = createMockLoader();
var xhr = { timeout: 5000 };
var file = new PCTAtlasFile(loader, 'atlas1', 'atlas.pct', null, null, null, xhr);
expect(file.config.textureXhrSettings).toBe(xhr);
});
it('should start with pending=1 and complete=false', function ()
{
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, 'atlas1', 'atlas.pct');
expect(file.pending).toBe(1);
expect(file.complete).toBe(false);
});
});
// -------------------------------------------------------------------------
// Constructor (plain object config)
// -------------------------------------------------------------------------
describe('constructor (plain object config)', function ()
{
it('should read key from the config object', function ()
{
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, { key: 'atlas2', atlasURL: 'atlas2.pct' });
expect(file.key).toBe('atlas2');
});
it('should read atlasURL from config when url is not present', function ()
{
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, { key: 'atlas2', atlasURL: 'level2.pct' });
expect(file.files[0].url).toBe('level2.pct');
});
it('should prefer url over atlasURL in config', function ()
{
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, {
key: 'atlas3',
url: 'preferred.pct',
atlasURL: 'fallback.pct'
});
expect(file.files[0].url).toBe('preferred.pct');
});
it('should read path, baseURL and textureXhrSettings from config', function ()
{
var loader = createMockLoader();
var xhr = { timeout: 3000 };
var file = new PCTAtlasFile(loader, {
key: 'atlas4',
atlasURL: 'atlas4.pct',
path: 'images/',
baseURL: 'http://cdn.test.com/',
textureXhrSettings: xhr
});
expect(file.config.path).toBe('images/');
expect(file.config.baseURL).toBe('http://cdn.test.com/');
expect(file.config.textureXhrSettings).toBe(xhr);
});
it('should pass the config xhrSettings to the PCT data file', function ()
{
var loader = createMockLoader();
var xhr = { async: true };
var file = new PCTAtlasFile(loader, {
key: 'atlas5',
atlasURL: 'atlas5.pct',
xhrSettings: xhr
});
expect(file.files[0].xhrSettings).toMatchObject(xhr);
});
});
// -------------------------------------------------------------------------
// PCT data file onProcess (decodes text into structured data)
// -------------------------------------------------------------------------
describe('PCT data file onProcess', function ()
{
it('should decode valid PCT text and store it on this.data', function ()
{
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, 'atlas', 'atlas.pct');
var dataFile = file.files[0];
// Mock XHR response with a minimal valid PCT payload
dataFile.xhrLoader = {
responseText:
'PCT:1.0\n' +
'P:atlas_0.png,RGBA8888,256,256,0\n' +
'logo|0|0,0,32,32\n'
};
// onProcessComplete is inherited from File; stub it so we don't need a real loader
dataFile.onProcessComplete = vi.fn();
dataFile.onProcessError = vi.fn();
dataFile.onProcess();
expect(dataFile.data).toBeDefined();
expect(Array.isArray(dataFile.data.pages)).toBe(true);
expect(dataFile.data.pages[0].filename).toBe('atlas_0.png');
expect(dataFile.data.frames.logo).toBeDefined();
expect(dataFile.onProcessComplete).toHaveBeenCalled();
expect(dataFile.onProcessError).not.toHaveBeenCalled();
});
it('should call onProcessError when the text fails to decode', function ()
{
// Silence the console.warn from the decoder
var warnSpy = vi.spyOn(console, 'warn').mockImplementation(function () {});
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, 'atlas', 'atlas.pct');
var dataFile = file.files[0];
dataFile.xhrLoader = { responseText: 'not a pct file' };
dataFile.onProcessComplete = vi.fn();
dataFile.onProcessError = vi.fn();
dataFile.onProcess();
expect(dataFile.onProcessError).toHaveBeenCalled();
expect(dataFile.onProcessComplete).not.toHaveBeenCalled();
warnSpy.mockRestore();
});
});
// -------------------------------------------------------------------------
// onFileComplete — queues ImageFiles based on decoded pages
// -------------------------------------------------------------------------
describe('onFileComplete', function ()
{
it('should decrement pending when a known file completes', function ()
{
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, 'atlas', 'atlas.pct');
var dataFile = file.files[0];
dataFile.data = { pages: [] };
expect(file.pending).toBe(1);
file.onFileComplete(dataFile);
expect(file.pending).toBe(0);
});
it('should not decrement pending for an unknown file', function ()
{
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, 'atlas', 'atlas.pct');
var unknown = { type: 'pct', data: { pages: [] } };
expect(file.pending).toBe(1);
file.onFileComplete(unknown);
expect(file.pending).toBe(1);
});
it('should create an ImageFile for each page declared in the decoded data', function ()
{
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, 'atlas', 'atlas.pct');
var dataFile = file.files[0];
dataFile.data = {
pages: [
{ filename: 'atlas_0.png' },
{ filename: 'atlas_1.png' }
]
};
file.onFileComplete(dataFile);
// files[0] = pct data file, files[1] and [2] = queued ImageFiles
expect(file.files.length).toBe(3);
expect(file.files[1].type).toBe('image');
expect(file.files[2].type).toBe('image');
});
it('should call loader.addFile for each queued ImageFile', function ()
{
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, 'atlas', 'atlas.pct');
var dataFile = file.files[0];
dataFile.data = {
pages: [
{ filename: 'atlas_0.png' },
{ filename: 'atlas_1.png' }
]
};
file.onFileComplete(dataFile);
expect(loader.addFile).toHaveBeenCalledTimes(2);
});
it('should generate image keys using multiKeyIndex and the page filename', function ()
{
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, 'atlas', 'atlas.pct');
var dataFile = file.files[0];
var expectedIndex = file.multiKeyIndex;
dataFile.data = { pages: [ { filename: 'sheet-0.png' } ] };
file.onFileComplete(dataFile);
expect(file.files[1].key).toBe('PCT' + expectedIndex + '_sheet-0.png');
});
it('should not queue any files when pages array is empty', function ()
{
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, 'atlas', 'atlas.pct');
var dataFile = file.files[0];
dataFile.data = { pages: [] };
file.onFileComplete(dataFile);
expect(file.files.length).toBe(1);
expect(loader.addFile).not.toHaveBeenCalled();
});
it('should not process when the completed file is not type pct', function ()
{
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, 'atlas', 'atlas.pct');
var dataFile = file.files[0];
// Override type so the onFileComplete condition fails
dataFile.type = 'image';
dataFile.data = { pages: [ { filename: 'atlas_0.png' } ] };
file.onFileComplete(dataFile);
expect(file.files.length).toBe(1);
expect(loader.addFile).not.toHaveBeenCalled();
});
it('should not process when data has no pages array', function ()
{
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, 'atlas', 'atlas.pct');
var dataFile = file.files[0];
dataFile.data = { frames: {} };
file.onFileComplete(dataFile);
expect(file.files.length).toBe(1);
expect(loader.addFile).not.toHaveBeenCalled();
});
// ---- baseURL / path / prefix override + restore ----------------------
it('should call loader.setBaseURL with the config baseURL during processing', function ()
{
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, 'atlas', 'atlas.pct', null, 'http://cdn.example.com/');
var dataFile = file.files[0];
dataFile.data = { pages: [ { filename: 'atlas_0.png' } ] };
file.onFileComplete(dataFile);
expect(loader.setBaseURL).toHaveBeenCalledWith('http://cdn.example.com/');
});
it('should call loader.setPath with the config path during processing', function ()
{
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, 'atlas', 'atlas.pct', 'custom/textures/');
var dataFile = file.files[0];
dataFile.data = { pages: [ { filename: 'atlas_0.png' } ] };
file.onFileComplete(dataFile);
expect(loader.setPath).toHaveBeenCalledWith('custom/textures/');
});
it('should restore the original loader baseURL after processing', function ()
{
var loader = createMockLoader();
loader.baseURL = 'http://original.com/';
var file = new PCTAtlasFile(loader, 'atlas', 'atlas.pct', null, 'http://cdn.override.com/');
var dataFile = file.files[0];
dataFile.data = { pages: [ { filename: 'atlas_0.png' } ] };
file.onFileComplete(dataFile);
var calls = loader.setBaseURL.mock.calls;
expect(calls[calls.length - 1][0]).toBe('http://original.com/');
});
it('should restore the original loader path after processing', function ()
{
var loader = createMockLoader();
loader.path = 'original/path/';
var file = new PCTAtlasFile(loader, 'atlas', 'atlas.pct', 'override/path/');
var dataFile = file.files[0];
dataFile.data = { pages: [ { filename: 'atlas_0.png' } ] };
file.onFileComplete(dataFile);
var calls = loader.setPath.mock.calls;
expect(calls[calls.length - 1][0]).toBe('original/path/');
});
it('should restore the original loader prefix after processing', function ()
{
var loader = createMockLoader();
loader.prefix = 'ORIGINAL.';
var file = new PCTAtlasFile(loader, 'atlas', 'atlas.pct');
var dataFile = file.files[0];
dataFile.data = { pages: [ { filename: 'atlas_0.png' } ] };
file.onFileComplete(dataFile);
var calls = loader.setPrefix.mock.calls;
expect(calls[calls.length - 1][0]).toBe('ORIGINAL.');
});
});
// -------------------------------------------------------------------------
// addToCache — writes to the atlas cache and the Texture Manager
// -------------------------------------------------------------------------
describe('addToCache', function ()
{
it('should not call addAtlasPCT when pending > 0', function ()
{
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, 'atlas', 'atlas.pct');
// Don't drop pending to 0 — isReadyToProcess returns false
file.addToCache();
expect(loader.textureManager.addAtlasPCT).not.toHaveBeenCalled();
});
it('should not write to the atlas cache when not ready to process', function ()
{
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, 'atlas', 'atlas.pct');
file.addToCache();
expect(loader.cacheManager.atlas.add).not.toHaveBeenCalled();
});
it('should not set complete when not ready to process', function ()
{
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, 'atlas', 'atlas.pct');
file.addToCache();
expect(file.complete).toBe(false);
});
it('should call addAtlasPCT when ready to process', function ()
{
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, 'atlas', 'atlas.pct');
var dataFile = file.files[0];
dataFile.data = makeTwoPageDecoded();
file.files.push({ type: 'image', key: 'PCT0_atlas_0.png', data: {} });
file.files.push({ type: 'image', key: 'PCT0_atlas_1.png', data: {} });
file.pending = 0;
file.addToCache();
expect(loader.textureManager.addAtlasPCT).toHaveBeenCalledTimes(1);
});
it('should write the decoded data to the atlas cache under the file key', function ()
{
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, 'myatlas', 'atlas.pct');
var dataFile = file.files[0];
var decoded = makeTwoPageDecoded();
dataFile.data = decoded;
file.files.push({ type: 'image', key: 'PCT0_atlas_0.png', data: {} });
file.files.push({ type: 'image', key: 'PCT0_atlas_1.png', data: {} });
file.pending = 0;
file.addToCache();
expect(loader.cacheManager.atlas.add).toHaveBeenCalledWith('myatlas', decoded);
});
it('should set complete to true after caching', function ()
{
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, 'atlas', 'atlas.pct');
var dataFile = file.files[0];
dataFile.data = makeTwoPageDecoded();
file.files.push({ type: 'image', key: 'PCT0_atlas_0.png', data: {} });
file.files.push({ type: 'image', key: 'PCT0_atlas_1.png', data: {} });
file.pending = 0;
file.addToCache();
expect(file.complete).toBe(true);
});
it('should pass the atlas key as the first argument to addAtlasPCT', function ()
{
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, 'level1', 'atlas.pct');
var dataFile = file.files[0];
dataFile.data = makeTwoPageDecoded();
file.files.push({ type: 'image', key: 'PCT0_atlas_0.png', data: {} });
file.files.push({ type: 'image', key: 'PCT0_atlas_1.png', data: {} });
file.pending = 0;
file.addToCache();
var args = loader.textureManager.addAtlasPCT.mock.calls[0];
expect(args[0]).toBe('level1');
});
it('should pass the images array in page order as the second argument', function ()
{
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, 'atlas', 'atlas.pct');
var dataFile = file.files[0];
var img0 = { tag: 'image-zero' };
var img1 = { tag: 'image-one' };
dataFile.data = makeTwoPageDecoded();
// Deliberately push the images out of order to verify that
// addToCache looks them up by filename, not by list position.
file.files.push({ type: 'image', key: 'PCT0_atlas_1.png', data: img1 });
file.files.push({ type: 'image', key: 'PCT0_atlas_0.png', data: img0 });
file.pending = 0;
file.addToCache();
var args = loader.textureManager.addAtlasPCT.mock.calls[0];
expect(args[1]).toEqual([ img0, img1 ]);
});
it('should pass the decoded data as the third argument', function ()
{
var loader = createMockLoader();
var file = new PCTAtlasFile(loader, 'atlas', 'atlas.pct');
var dataFile = file.files[0];
var decoded = makeTwoPageDecoded();
dataFile.data = decoded;
file.files.push({ type: 'image', key: 'PCT0_atlas_0.png', data: {} });
file.files.push({ type: 'image', key: 'PCT0_atlas_1.png', data: {} });
file.pending = 0;
file.addToCache();
var args = loader.textureManager.addAtlasPCT.mock.calls[0];
expect(args[2]).toBe(decoded);
});
it('should strip the PCT{idx}_ marker when matching file keys to filenames', function ()
{
var loader = createMockLoader();
// Force a non-zero multiKeyIndex so the marker is PCT5_, not PCT0_
loader.multiKeyIndex = 5;
var file = new PCTAtlasFile(loader, 'atlas', 'atlas.pct');
var dataFile = file.files[0];
var imgData = { width: 512, height: 512 };
dataFile.data = {
pages: [ { filename: 'spritesheet-big.png' } ],
folders: [],
frames: {}
};
file.files.push({
type: 'image',
key: 'PCT5_spritesheet-big.png',
data: imgData
});
file.pending = 0;
file.addToCache();
var args = loader.textureManager.addAtlasPCT.mock.calls[0];
expect(args[1]).toEqual([ imgData ]);
});
});
});