diff --git a/pages.json b/pages.json index 37ff6b342ab5da16ddaf195b860614d81a1063c2..d6db00db43c80561451a8ad6519eb1342a06efa0 100644 --- a/pages.json +++ b/pages.json @@ -329,7 +329,14 @@ "style": { "navigationBarTitleText": "数据存储" } - }, + }, + { + "path": "pages/API/filemanager/filemanager", + "style": { + "navigationBarTitleText": "沙盒文件管理" + } + }, + { "path": "pages/API/action-sheet/action-sheet", "style": { diff --git a/pages/API/filemanager/filemanager.test.js b/pages/API/filemanager/filemanager.test.js new file mode 100644 index 0000000000000000000000000000000000000000..570f4a7846b7f96e6cb510706ddabc90aa8d6148 --- /dev/null +++ b/pages/API/filemanager/filemanager.test.js @@ -0,0 +1,565 @@ +const PAGE_PATH = '/pages/API/filemanager/filemanager' + + +describe('ExtApi-FileManagerTest', () => { + + let page; + function getData(key = '') { + return new Promise(async (resolve, reject) => { + const data = await page.data() + resolve(key ? data[key] : data) + }) + } + + + beforeAll(async () => { + page = await program.reLaunch(PAGE_PATH) + await page.waitFor(600); + }); + + async function isDone() { + let isDone = await page.waitFor(async () => { + return await page.data('done') + }) + await page.setData({done: false}) + return isDone + } + + it('USER_DATA_PATH test', async () => { + // 测试 USER_DATA_PATH + let globalUserDataPath = await getData('globalUserDataPath') + + await page.setData({ + recursiveVal: true, + copyToBasePath:globalUserDataPath, + basePath: globalUserDataPath, + rmDirFile:'a', + unlinkFile:'a/1.txt' + }) + + // 先清除文件,需要清除全部可能存在的历史测试文件,避免运行失败 + const btnUnLinkFileButton = await page.$('.btn-unlink-file') + await btnUnLinkFileButton.tap() + await isDone() + + await page.setData({ + unlinkFile:'a/2.txt' + }) + await btnUnLinkFileButton.tap() + await isDone() + + await page.setData({ + unlinkFile:'a/3.txt' + }) + await btnUnLinkFileButton.tap() + await isDone() + + // 清除文件夹 + const btnRmDirButton = await page.$('.btn-remove-dir') + await btnRmDirButton.tap() + await isDone() + // 重新创建测试目录 + const btnMkdDirButton = await page.$('.btn-mkdir') + await btnMkdDirButton.tap() + await isDone() + + const btnReadDirButton = await page.$('.btn-read-dir') + await btnReadDirButton.tap() + await isDone() + + + // 期望通过 recursive = true的 文件夹删除,得到一个空的 /a 目录 + let fileListComplete = await getData('fileListComplete') + expect(JSON.stringify(fileListComplete)).toEqual('[]') + let fileListSuccess = await getData('fileListSuccess') + expect(JSON.stringify(fileListSuccess)).toEqual('[]') + + // 先测试 recursive = false 文件夹创建,期望失败 + await page.setData({ + recursiveVal: false, + mkdirFile:'a/b/c' + }) + + + await btnMkdDirButton.tap() + await isDone() + + + let lastFailError = await getData('lastFailError') + expect(lastFailError.errCode).toEqual(1300002) + expect(lastFailError.errMsg).toEqual('no such file or directory:unifile://usr/a/b/c') + let lastCompleteError = await getData('lastCompleteError') + expect(lastCompleteError.errCode).toEqual(1300002) + expect(lastCompleteError.errMsg).toEqual('no such file or directory:unifile://usr/a/b/c') + + + // 测试 recursive = true 期望文件夹创建成功 + await page.setData({ + recursiveVal: true + }) + await btnMkdDirButton.tap() + await isDone() + + await btnReadDirButton.tap() + await isDone() + + // 期望通过 recursive = true的 文件夹删除,得到一个空的 /a 目录 + fileListComplete = await getData('fileListComplete') + expect(JSON.stringify(fileListComplete)).toEqual("[\"b\"]") + fileListSuccess = await getData('fileListSuccess') + expect(JSON.stringify(fileListSuccess)).toEqual("[\"b\"]") + + // 测试写入文件 + const btnWriteFileButton = await page.$('.btn-write-file') + await btnWriteFileButton.tap() + await isDone() + // 检查目录列表数量 + await btnReadDirButton.tap() + await isDone() + fileListComplete = await getData('fileListComplete') + expect(JSON.stringify(fileListComplete)).toEqual("[\"b\",\"1.txt\"]") + fileListSuccess = await getData('fileListSuccess') + expect(JSON.stringify(fileListSuccess)).toEqual("[\"b\",\"1.txt\"]") + // 获取和对比 文件内容 + const btnReadFileButton = await page.$('.btn-read-file') + await btnReadFileButton.tap() + await isDone() + let readFileRet = await getData('readFileRet') + expect(readFileRet).toEqual("锄禾日当午,汗滴禾下土,谁知盘中餐,粒粒皆辛苦") + + // 更换文件内容 获取和对比 文件md5和sha1 + await page.setData({ + writeFileContent: "If you were a teardrop;In my eye,For fear of losing you,I would never cry.And if the golden sun,Should cease to shine its light,Just one smile from you,Would make my whole world bright.", + getFileInfoAlgorithm:"md5" + }) + await btnWriteFileButton.tap() + await isDone() + + await btnReadFileButton.tap() + await isDone() + readFileRet = await getData('readFileRet') + expect(readFileRet).toEqual("If you were a teardrop;In my eye,For fear of losing you,I would never cry.And if the golden sun,Should cease to shine its light,Just one smile from you,Would make my whole world bright.") + + const btnGetFileInfoButton = await page.$('.btn-get-file-info') + await btnGetFileInfoButton.tap() + await isDone() + + let getFileInfoSize = await getData('getFileInfoSize') + expect(getFileInfoSize).toEqual(185) + let getFileInfoDigest = await getData('getFileInfoDigest') + expect(getFileInfoDigest).toEqual("29ddd02ed3c38ccebb98884eda082cb1") + // 切换为 sha1 + await page.setData({ + getFileInfoAlgorithm:"sha1" + }) + + await btnGetFileInfoButton.tap() + await isDone() + + getFileInfoSize = await getData('getFileInfoSize') + expect(getFileInfoSize).toEqual(185) + getFileInfoDigest = await getData('getFileInfoDigest') + expect(getFileInfoDigest).toEqual("ebef4e75783e0db499fc260d120e695005bead8a") + + // 测试 copyfile + await page.setData({ + + copyFromFile:"a/1.txt", + copyToFile:"a/2.txt" + }) + const btnCopyFileButton = await page.$('.btn-copy-file') + await btnCopyFileButton.tap() + await isDone() + + + await btnReadDirButton.tap() + await isDone() + + // 1.txt 2.txt 两个文件都存在 + fileListComplete = await getData('fileListComplete') + expect(JSON.stringify(fileListComplete)).toEqual("[\"b\",\"1.txt\",\"2.txt\"]") + fileListSuccess = await getData('fileListSuccess') + expect(JSON.stringify(fileListSuccess)).toEqual("[\"b\",\"1.txt\",\"2.txt\"]") + + // 测试 rename + await page.setData({ + renameFromFile:"a/2.txt", + renameToFile:"a/3.txt" + }) + + const btnRenameFileButton = await page.$('.btn-rename-file') + await btnRenameFileButton.tap() + await isDone() + + await btnReadDirButton.tap() + await isDone() + + // 1.txt 3.txt 两个文件都存在 + fileListComplete = await getData('fileListComplete') + expect(JSON.stringify(fileListComplete)).toEqual("[\"b\",\"1.txt\",\"3.txt\"]") + fileListSuccess = await getData('fileListSuccess') + expect(JSON.stringify(fileListSuccess)).toEqual("[\"b\",\"1.txt\",\"3.txt\"]") + + + }); + + it('TEMP_PATH test', async () => { + // 测试 TEMP_PATH + let globalTempPath = await getData('globalTempPath') + await page.setData({ + recursiveVal: true, + basePath: globalTempPath, + copyToBasePath:globalTempPath, + rmDirFile:'a', + unlinkFile:'a/我们经历了一场兵慌马乱的战争.1@2#3$4%5^6&7*8(9)0+-qwertyuiopasdfghjklzxcvbnm;,/中文路径/张三/name/中文文件.mock' + }) + + + // 先清除文件,需要清除全部可能存在的历史测试文件,避免运行失败 + const btnUnLinkFileButton = await page.$('.btn-unlink-file') + await btnUnLinkFileButton.tap() + await isDone() + + await page.setData({ + unlinkFile:'a/提前创建的目录/4.txt' + }) + await btnUnLinkFileButton.tap() + await isDone() + + + // 清除文件夹 + const btnRmDirButton = await page.$('.btn-remove-dir') + await btnRmDirButton.tap() + await isDone() + + // 重新创建测试目录 + const btnMkdDirButton = await page.$('.btn-mkdir') + await btnMkdDirButton.tap() + await isDone() + + const btnReadDirButton = await page.$('.btn-read-dir') + await btnReadDirButton.tap() + await isDone() + + + // 期望通过 recursive = true的 文件夹删除,得到一个空的 /a 目录 + let fileListComplete = await getData('fileListComplete') + expect(JSON.stringify(fileListComplete)).toEqual("[\"b\"]") + let fileListSuccess = await getData('fileListSuccess') + expect(JSON.stringify(fileListSuccess)).toEqual("[\"b\"]") + + // 测试 创建多层级文件目录 + await page.setData({ + recursiveVal: true, + mkdirFile:'a/b/c/d/e/f/g/h/i/g/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/中文路径/张三/test', + }) + + await btnMkdDirButton.tap() + await isDone() + + await btnReadDirButton.tap() + await isDone() + + fileListComplete = await getData('fileListComplete') + expect(JSON.stringify(fileListComplete)).toEqual("[\"b\"]") + fileListSuccess = await getData('fileListSuccess') + expect(JSON.stringify(fileListSuccess)).toEqual("[\"b\"]") + + // 测试 创建包含中文特殊符号的目录 + await page.setData({ + recursiveVal: true, + mkdirFile:'a/我们经历了一场兵慌马乱的战争.1@2#3$4%5^6&7*8(9)0+-qwertyuiopasdfghjklzxcvbnm;,/中文路径/张三/name', + }) + await btnMkdDirButton.tap() + await isDone() + + await btnReadDirButton.tap() + await isDone() + + // 期望通过 recursive = true的 文件夹删除,得到一个空的 /a 目录 + fileListComplete = await getData('fileListComplete') + expect(JSON.stringify(fileListComplete)).toEqual("[\"b\",\"我们经历了一场兵慌马乱的战争.1@2#3$4%5^6&7*8(9)0+-qwertyuiopasdfghjklzxcvbnm;,\"]") + fileListSuccess = await getData('fileListSuccess') + expect(JSON.stringify(fileListSuccess)).toEqual("[\"b\",\"我们经历了一场兵慌马乱的战争.1@2#3$4%5^6&7*8(9)0+-qwertyuiopasdfghjklzxcvbnm;,\"]") + + /** + * 从资源文件中读取图片为base64,测试写入较大文件场景 + * 'static/list-mock/safe.png' 注意,依赖这个资源文件,不能删除 + */ + let globalAppResourcePath = await getData('globalAppResourcePath') + await page.setData({ + + basePath: globalAppResourcePath, + readFile:'static/list-mock/safe.png', + readFileFlag:'base64' + }) + + + // 获取和对比 文件内容 + const btnReadFileButton = await page.$('.btn-read-file') + await btnReadFileButton.tap() + await isDone() + let readFileRet = await getData('readFileRet') + + expect(readFileRet.length).toEqual(426128) + let endStr = readFileRet.substring(readFileRet.length - 10) + expect(endStr).toEqual("lFTkSuQmCC") + + await page.setData({ + basePath: globalTempPath, + writeFile:'a/我们经历了一场兵慌马乱的战争.1@2#3$4%5^6&7*8(9)0+-qwertyuiopasdfghjklzxcvbnm;,/中文路径/张三/name/中文文件.mock', + writeFileContent:readFileRet + }) + + + const btnWriteFileButton = await page.$('.btn-write-file') + await btnWriteFileButton.tap() + await isDone() + + // 获取文件列表,判断是否写入成功,同时置空base64内容 避免影响实时查看状态 + await page.setData({ + readDir:'a/我们经历了一场兵慌马乱的战争.1@2#3$4%5^6&7*8(9)0+-qwertyuiopasdfghjklzxcvbnm;,/中文路径/张三/name', + readFileRet:'', + writeFileContent:'' + }) + + // 检查目录列表数量 + await btnReadDirButton.tap() + await isDone() + fileListComplete = await getData('fileListComplete') + expect(JSON.stringify(fileListComplete)).toEqual("[\"中文文件.mock\"]") + fileListSuccess = await getData('fileListSuccess') + expect(JSON.stringify(fileListSuccess)).toEqual("[\"中文文件.mock\"]") + + + // 更换文件内容 获取和对比 文件md5和sha1 + await page.setData({ + getFileInfoFile:'a/我们经历了一场兵慌马乱的战争.1@2#3$4%5^6&7*8(9)0+-qwertyuiopasdfghjklzxcvbnm;,/中文路径/张三/name/中文文件.mock', + getFileInfoAlgorithm:"md5", + }) + + const btnGetFileInfoButton = await page.$('.btn-get-file-info') + await btnGetFileInfoButton.tap() + await isDone() + + let getFileInfoSize = await getData('getFileInfoSize') + expect(getFileInfoSize).toEqual(426128) + let getFileInfoDigest = await getData('getFileInfoDigest') + expect(getFileInfoDigest).toEqual("795fd5a20b4f0a63504330e9132dcd30") + + // 切换为 sha1 + await page.setData({ + getFileInfoAlgorithm:"sha1" + }) + + await btnGetFileInfoButton.tap() + await isDone() + + getFileInfoSize = await getData('getFileInfoSize') + expect(getFileInfoSize).toEqual(426128) + getFileInfoDigest = await getData('getFileInfoDigest') + expect(getFileInfoDigest).toEqual("74877928eddd0351bd8b3b1c677b56f25db682fc") + + // 测试不支持的摘要算法,期望返回错误 + await page.setData({ + getFileInfoAlgorithm:"sha256" + }) + + await btnGetFileInfoButton.tap() + await isDone() + + let lastFailError = await getData('lastFailError') + expect(lastFailError.errCode).toEqual(1300022) + let lastCompleteError = await getData('lastCompleteError') + expect(lastCompleteError.errCode).toEqual(1300022) + + + // rename 到一个没有提前创建过的目录,期望返回错误 + await page.setData({ + renameFromFile:"a/我们经历了一场兵慌马乱的战争.1@2#3$4%5^6&7*8(9)0+-qwertyuiopasdfghjklzxcvbnm;,/中文路径/张三/name/中文文件.mock", + renameToFile:"a/没有提前创建的目录/3.txt" + }) + + const btnRenameFileButton = await page.$('.btn-rename-file') + await btnRenameFileButton.tap() + await isDone() + + lastFailError = await getData('lastFailError') + expect(lastFailError.errCode).toEqual(1300002) + lastCompleteError = await getData('lastCompleteError') + expect(lastCompleteError.errCode).toEqual(1300002) + + // 非递归创建一级目录。期望成功 + await page.setData({ + recursiveVal: false, + mkdirFile:'a/提前创建的目录', + }) + + await btnMkdDirButton.tap() + await isDone() + + await page.setData({ + readDir:'a', + }) + + await btnReadDirButton.tap() + await isDone() + + fileListComplete = await getData('fileListComplete') + expect(JSON.stringify(fileListComplete)).toEqual("[\"b\",\"我们经历了一场兵慌马乱的战争.1@2#3$4%5^6&7*8(9)0+-qwertyuiopasdfghjklzxcvbnm;,\",\"提前创建的目录\"]") + fileListSuccess = await getData('fileListSuccess') + expect(JSON.stringify(fileListSuccess)).toEqual("[\"b\",\"我们经历了一场兵慌马乱的战争.1@2#3$4%5^6&7*8(9)0+-qwertyuiopasdfghjklzxcvbnm;,\",\"提前创建的目录\"]") + + + await page.setData({ + + copyFromFile:"a/我们经历了一场兵慌马乱的战争.1@2#3$4%5^6&7*8(9)0+-qwertyuiopasdfghjklzxcvbnm;,/中文路径/张三/name/中文文件.mock", + copyToFile:"a/提前创建的目录/4.txt" + }) + + const btnCopyFileButton = await page.$('.btn-copy-file') + await btnCopyFileButton.tap() + await isDone() + + await page.setData({ + readDir:'a/提前创建的目录', + }) + + await btnReadDirButton.tap() + await isDone() + + fileListComplete = await getData('fileListComplete') + expect(JSON.stringify(fileListComplete)).toEqual("[\"4.txt\"]") + fileListSuccess = await getData('fileListSuccess') + expect(JSON.stringify(fileListSuccess)).toEqual("[\"4.txt\"]") + + await page.setData({ + unlinkFile:'a/提前创建的目录/4.txt' + }) + await btnUnLinkFileButton.tap() + await isDone() + + await btnReadDirButton.tap() + await isDone() + + fileListComplete = await getData('fileListComplete') + expect(JSON.stringify(fileListComplete)).toEqual("[]") + fileListSuccess = await getData('fileListSuccess') + expect(JSON.stringify(fileListSuccess)).toEqual("[]") + + + }); + + + + + it('CROSS DIR test', async () => { + /** + * 跨越用户目录和代码资源目录 + */ + let globalRootPath = await getData('globalRootPath') + await page.setData({ + recursiveVal: true, + basePath: globalRootPath, + readDir:'a', + rmDirFile:'a', + mkdirFile:'a', + accessFile:'a/从代码目录拷贝的资源.png', + unlinkFile:'a/从代码目录拷贝的资源.png' + }) + + + // 先清除文件,需要清除全部可能存在的历史测试文件,避免运行失败 + const btnUnLinkFileButton = await page.$('.btn-unlink-file') + await btnUnLinkFileButton.tap() + await isDone() + + + // 清除文件夹 + const btnRmDirButton = await page.$('.btn-remove-dir') + await btnRmDirButton.tap() + await isDone() + + // 重新创建测试目录,期望通过 recursive = true的 文件夹删除,得到一个空的 /a 目录 + const btnMkdDirButton = await page.$('.btn-mkdir') + await btnMkdDirButton.tap() + await isDone() + + const btnReadDirButton = await page.$('.btn-read-dir') + await btnReadDirButton.tap() + await isDone() + + let fileListComplete = await getData('fileListComplete') + expect(JSON.stringify(fileListComplete)).toEqual('[]') + let fileListSuccess = await getData('fileListSuccess') + expect(JSON.stringify(fileListSuccess)).toEqual('[]') + + + // 检查资源文件,此时不存在 + const btnAccessFileButton = await page.$('.btn-access-file') + await btnAccessFileButton.tap() + await isDone() + + let accessFileRet = await getData("accessFileRet") + expect(accessFileRet).toEqual('') + + + // 准备从资源目录拷贝png + let globalAppResourcePath = await getData('globalAppResourcePath') + await page.setData({ + basePath: globalAppResourcePath, + unlinkFile:'static/list-mock/safe.png', + accessFile:'static/list-mock/safe.png', + }) + // 检查资源文件,期望存在 + await btnAccessFileButton.tap() + await isDone() + + accessFileRet = await getData("accessFileRet") + expect(accessFileRet).toEqual('access:ok') + + // 尝试删除资源,期望失败 + await btnUnLinkFileButton.tap() + await isDone() + + await btnAccessFileButton.tap() + await isDone() + + accessFileRet = await getData("accessFileRet") + expect(accessFileRet).toEqual('access:ok') + // 复制资源到 root目录 + await page.setData({ + copyToBasePath:globalRootPath, + copyFromFile:"static/list-mock/safe.png", + copyToFile:"a/从代码目录拷贝的资源.png" + }) + const btnCopyFileButton = await page.$('.btn-copy-file') + await btnCopyFileButton.tap() + await isDone() + + // 检查期望 root 目录中图片文件存在 + await page.setData({ + basePath:globalRootPath, + unlinkFile:'a/从代码目录拷贝的资源.png', + accessFile:'a/从代码目录拷贝的资源.png', + }) + await btnAccessFileButton.tap() + await isDone() + + accessFileRet = await getData("accessFileRet") + expect(accessFileRet).toEqual('access:ok') + + await btnUnLinkFileButton.tap() + await isDone() + + await btnAccessFileButton.tap() + await isDone() + + accessFileRet = await getData("accessFileRet") + expect(accessFileRet).toEqual('') + + }); + + + +}); diff --git a/pages/API/filemanager/filemanager.uvue b/pages/API/filemanager/filemanager.uvue new file mode 100644 index 0000000000000000000000000000000000000000..0433ca4b14353294be4227dfb562288e1d3d5cd1 --- /dev/null +++ b/pages/API/filemanager/filemanager.uvue @@ -0,0 +1,318 @@ + + + + + diff --git a/pages/tabBar/API.uvue b/pages/tabBar/API.uvue index bc0a6e4586c0b8d413a343ec05de7029328e813a..624c7986f2b63d8afc3693b2a927ec57841e22cc 100644 --- a/pages/tabBar/API.uvue +++ b/pages/tabBar/API.uvue @@ -397,6 +397,10 @@ name: 'storage(key-value存储)', url: 'storage', api: ["getStorageInfo", "getStorageInfoSync", "getStorage", "getStorageSync", "setStorage", "setStorageSync", "removeStorage", "removeStorageSync", "clearStorage", "clearStorageSync"] + }, + { + name: '沙盒文件管理', + url: 'filemanager' }, ] as Page[], },