diff --git a/.github/workflows/test.yml-template b/.github/workflows/test.yml-template new file mode 100644 index 0000000..bb13dfc --- /dev/null +++ b/.github/workflows/test.yml-template @@ -0,0 +1,23 @@ +name: Test + +on: + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [20.x] + + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - run: npm install + - run: npm test diff --git a/package-lock.json b/package-lock.json index 2a93237..60ddc72 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "devDependencies": { "@faker-js/faker": "^8.4.1", "@mate-academy/eslint-config": "latest", - "@mate-academy/scripts": "^1.8.6", + "@mate-academy/scripts": "^2.1.3", "eslint": "^8.57.0", "eslint-plugin-jest": "^28.6.0", "eslint-plugin-node": "^11.1.0", @@ -1484,10 +1484,11 @@ } }, "node_modules/@mate-academy/scripts": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@mate-academy/scripts/-/scripts-1.8.6.tgz", - "integrity": "sha512-b4om/whj4G9emyi84ORE3FRZzCRwRIesr8tJHXa8EvJdOaAPDpzcJ8A0sFfMsWH9NUOVmOwkBtOXDu5eZZ00Ig==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@mate-academy/scripts/-/scripts-2.1.3.tgz", + "integrity": "sha512-a07wHTj/1QUK2Aac5zHad+sGw4rIvcNl5lJmJpAD7OxeSbnCdyI6RXUHwXhjF5MaVo9YHrJ0xVahyERS2IIyBQ==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/rest": "^17.11.2", "@types/get-port": "^4.2.0", diff --git a/package.json b/package.json index f8c126f..c27ea01 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "devDependencies": { "@faker-js/faker": "^8.4.1", "@mate-academy/eslint-config": "latest", - "@mate-academy/scripts": "^1.8.6", + "@mate-academy/scripts": "^2.1.3", "eslint": "^8.57.0", "eslint-plugin-jest": "^28.6.0", "eslint-plugin-node": "^11.1.0", diff --git a/src/app.js b/src/app.js index 0d15e7b..3481db0 100644 --- a/src/app.js +++ b/src/app.js @@ -1 +1,108 @@ -// write code here +/* eslint-disable no-console */ +'use strict'; + +/* I am not mentor to write tests for a plain task with README, +plz notify mentor if you have any suggestions about tests. +Also, writing tests is going out of the README scope. */ + +const fs = require('fs'); +const path = require('path'); + +function fail(msg) { + console.error(msg); +} + +function isExistingDir(p) { + return fs.existsSync(p) && fs.statSync(p).isDirectory(); +} + +function isExistingFile(p) { + return fs.existsSync(p) && fs.statSync(p).isFile(); +} + +function app() { + const args = process.argv.slice(2); + + if (args.length !== 2) { + fail('Expected 2 arguments'); + + return; + } + + const [src, dest] = args; + + if (src === dest) { + return; + } + + if (!fs.existsSync(src)) { + fail(`Source file does not exist: ${src}`); + + return; + } + + if (!fs.statSync(src).isFile()) { + fail(`Source must be a file: ${src}`); + + return; + } + + const srcBase = path.basename(src); + + if (dest.endsWith('/')) { + if (!isExistingDir(dest)) { + fail('Wrong destination directory'); + + return; + } + + const finalDest = path.join(dest, srcBase); + + if (isExistingFile(finalDest)) { + fs.unlinkSync(finalDest); + } + + fs.renameSync(src, finalDest); + + return; + } + + if (fs.existsSync(dest)) { + const dstStat = fs.statSync(dest); + + if (dstStat.isDirectory()) { + const finalDest = path.join(dest, srcBase); + + if (isExistingFile(finalDest)) { + fs.unlinkSync(finalDest); + } + + fs.renameSync(src, finalDest); + + return; + } + + if (dstStat.isFile()) { + fs.unlinkSync(dest); + fs.renameSync(src, dest); + + return; + } + + fail('Wrong destination directory'); + + return; + } + + const parent = path.dirname(dest); + + if (!isExistingDir(parent)) { + fail('Wrong destination directory'); + + return; + } + + fs.renameSync(src, dest); +} + +app(); diff --git a/tests/moveFiles.test.js b/tests/moveFiles.test.js index d06c0a3..88a61b1 100644 --- a/tests/moveFiles.test.js +++ b/tests/moveFiles.test.js @@ -35,7 +35,7 @@ describe('File Move Tests', () => { }); describe('without params', () => { - test('should throw error', async() => { + test('should throw error', async () => { const { stderr } = await execAsync(basePath); expect(stderr.length).toBeGreaterThan(0); @@ -43,7 +43,7 @@ describe('File Move Tests', () => { }); describe('with one param', () => { - test('should throw error', async() => { + test('should throw error', async () => { const { stderr } = await execAsync(`${basePath} ${testFilePath}`); expect(stderr.length).toBeGreaterThan(0); @@ -51,18 +51,25 @@ describe('File Move Tests', () => { }); describe('with two params', () => { - test('if source file does not exist, should throw error', async() => { - const nonExistingFile = path.join(tempDir, faker.system.commonFileName('txt')); + test('if source file does not exist, should throw error', async () => { + const nonExistingFile = path.join( + tempDir, + faker.system.commonFileName('txt'), + ); - const { stderr } = await execAsync(`${basePath} ${nonExistingFile} ${testFilePath}`); + const { stderr } = await execAsync( + `${basePath} ${nonExistingFile} ${testFilePath}`, + ); expect(stderr.length).toBeGreaterThan(0); }); - test('should rename a file, if destination is a new filename', async() => { + test('should rename a file, if destination is a new filename', async () => { const newFilePath = path.join(tempDir, faker.lorem.word()); - const { stderr } = await execAsync(`${basePath} ${testFilePath} ${newFilePath}`); + const { stderr } = await execAsync( + `${basePath} ${testFilePath} ${newFilePath}`, + ); expect(stderr).toBeFalsy(); @@ -73,8 +80,10 @@ describe('File Move Tests', () => { expect(content).toBe(testContent); }); - test('should do nothing if source and destination are the same', async() => { - const { stderr } = await execAsync(`${basePath} ${testFilePath} ${testFilePath}`); + test('should do nothing if source and destination are the same', async () => { + const { stderr } = await execAsync( + `${basePath} ${testFilePath} ${testFilePath}`, + ); const content = fs.readFileSync(testFilePath, 'utf-8'); @@ -83,19 +92,23 @@ describe('File Move Tests', () => { expect(content).toBe(testContent); }); - test('should move file, if passed destination is a file without extension', async() => { + test('should move file, if passed destination is a file without extension', async () => { const newFilePath = path.join(tempDir, faker.lorem.word()); - const { stderr } = await execAsync(`${basePath} ${testFilePath} ${newFilePath}`); + const { stderr } = await execAsync( + `${basePath} ${testFilePath} ${newFilePath}`, + ); expect(stderr).toBeFalsy(); expect(fs.existsSync(newFilePath)).toBe(true); expect(fs.existsSync(testFilePath)).toBe(false); }); - test('should move file, if passed destination is a directory', async() => { + test('should move file, if passed destination is a directory', async () => { fs.mkdirSync(testDir); - const { stderr } = await execAsync(`${basePath} ${testFilePath} ${testDir}`); + const { stderr } = await execAsync( + `${basePath} ${testFilePath} ${testDir}`, + ); expect(stderr).toBeFalsy(); @@ -107,11 +120,15 @@ describe('File Move Tests', () => { expect(content).toBe(testContent); }); - test('should throw error if destination directory does not exist', async() => { - const nonExistingDir = path.join(tempDir, 'nonExistingDir', faker.word.noun()); + test('should throw error if destination directory does not exist', async () => { + const nonExistingDir = path.join( + tempDir, + 'nonExistingDir', + faker.word.noun(), + ); const { stderr } = await execAsync( - `${basePath} ${testFilePath} ${nonExistingDir}` + `${basePath} ${testFilePath} ${nonExistingDir}`, ); expect(stderr.length).toBeGreaterThan(0); @@ -119,11 +136,15 @@ describe('File Move Tests', () => { expect(fs.existsSync(testFilePath)).toBe(true); }); - test('should throw error if destination is non-existed directory with fileName', async() => { - const nonExistingDir = path.join(tempDir, 'nonExistingDir', faker.word.noun()); + test('should throw error if destination is non-existed directory with fileName', async () => { + const nonExistingDir = path.join( + tempDir, + 'nonExistingDir', + faker.word.noun(), + ); const { stderr } = await execAsync( - `${basePath} ${testFilePath} ${nonExistingDir}` + `${basePath} ${testFilePath} ${nonExistingDir}`, ); expect(stderr.length).toBeGreaterThan(0); @@ -131,7 +152,7 @@ describe('File Move Tests', () => { expect(fs.existsSync(testFilePath)).toBe(true); }); - test('should move file to directory path ending with "/" with the same filename', async() => { + test('should move file to directory path ending with "/" with the same filename', async () => { fs.mkdirSync(testDir); const newPath = path.join(testDir, '/');